WIP: Access method extendability

Started by Alexander Korotkovabout 11 years ago97 messages
#1Alexander Korotkov
aekorotkov@gmail.com
3 attachment(s)

Hackers,

Postgres was initially designed to support access methods extendability.
This extendability lives to present day. However, this is mostly internal
in-core extendability. One can quite easily add new access method into
PostgreSQL core. But if one try to implement access method as external
module, he will be faced with following difficulties:

1. Need to directly insert into pg_am, because of no "CREATE ACCESS
METHOD" command. And no support of dependencies between am and opclasses
etc.
2. Module can't define xlog records. So, new am would be not WAL-logged.

The first problem is purely mechanical. Nothing prevents us to implement
"CREATE ACCESS METHOD" and "DROP ACCESS METHOD" commands and support all
required dependencies.

Problem of WAL is a bit more complex. According to previous discussions, we
don't want to let extensions declare their own xlog records. If we let them
then recovery process will depend on extensions. That is much violates
reliability. Solution is to implement some generic xlog record which is
able to represent difference between blocks in some general manner.

3 patches are attached:

1. CREATE/DROP ACCESS METHOD commands. With support of dependencies.
2. New generic xlog record type.
3. Bloom contrib module as example of usage of previous two features.
This module was posted few years ago by Teodor Sigaev. Now, it's wrapped as
an extension and WAL-logged.

Patches are in WIP state. No documentation and very little of comments.
However, it believe that it's enough to do some general concept review.

Some notes about generic xlog format. Generic xlog represent difference
between pages as operations set of two kinds:

1. Move memory inside the page. Optional flag is to zero gap on a
previous memory location.
2. Copy memory fragment of memory from xlog record to page. As an option
bitwise logical operations are supported as well as plain copy.

Generic xlog can open page in two modes:

1. Create mode: page is zeroed independent on its lsn.
2. Update mode: page is updated only if it's lsn is lower than record lsn

Usually, xlog record is filled in critical sections when memory allocations
is prohibited. Thus, user have to previously initialize it with knowledge
of pages count, total operations count and total length of data.

------
With best regards,
Alexander Korotkov.

Attachments:

create-am.1.patch.gzapplication/x-gzip; name=create-am.1.patch.gzDownload
generic-xlog.1.patch.gzapplication/x-gzip; name=generic-xlog.1.patch.gzDownload
bloom-contrib.1.patch.gzapplication/x-gzip; name=bloom-contrib.1.patch.gzDownload
��Z>T��{w����7�)��;2@�`���u(�y��!��g<�XM�I�D���$���~33~;���
���3���e@=��2###v��<��}����n�L&w��������O'?L��N���K�n�NJ����i��i]��f�����7Es����)�I���u���5���s����o��%��y���Ar?���R���?p�������$[[�������[�n_,�OO&�����g'���;��;��d>����7�����]89�{������&����x]����������k�������'E���Vzn��q���\���R�w[��g�m_%��lh�������G������s#.p���?/�������6;]L�������4�������C��h��'���?>~V�/�v���?��v����A���z�?�������_O���}�����������t29:�.������8��,f�N'��������bz��~���r�;�������fo��o����/���Ax������}�����������w������w��x?=9q?��;�?�����6;=:�p��O�)|��d�;tq��xv�u�;������������{{�����]��q�{w239�t��&\�G���]����Z�N������=��$gv�)n���M}�V�i`�s�Z	�*I�����|���x��B�l�8�0?_�.y5����������d{���������n��d�.�fr��8��+��������>,.���Z���d���=�<�~~>_��o�L������dv�w4��p�{6_N�k��-��Ir������w���,���g����������\��I���{wIrq���_w|q���O� ���d������O��K^���]�L�O>�&n�?��M?MN�\����������E�3�����$9�}�-'�m�"�����d��/�����_\o/N��J��7���O���~L&K�����uQ��������?
���������������5��N��{��������^�������,����'�L���������'���^��L,�s{�p�{��tC2qS�l�X�����x�0���t��N�0�OO>����K�<�v��q��������&����t�	����������&�s�B���0j���&^���_�{Y8�N�����#��t�3=]�&���}�tr���lz�v~�>�?n���Y,'��0��������xz�4�i�d������?��L?�v�,��������~�<��������f?����������]���p��]�%����'�d���m��O�8y8�O�n�.�2���w�D9q���u���������=����
���pq�f��G��G?�;�����~:9s�����>x��� ����������LO�-�'�n�]�i����#�N�S��V;���l'���V�kz1�-+s{1].���~���O��,����g�G��m���K�u���?<��C�}��<~�h�O&������.���lw�������\|����}��j�?*����9?����={��Zq�=M���-GS �i���R��i���
��xu�9��7�������-���
���3e8�U�+���qk���SJ�y������m����������73?����^h������w�L�M���F{��PS�Zo�X��.�w����]5X��MO}���EP'���[2�����]7&;;�����������1�����������������������1K?��������'+�������#�8�RAN=�%�d�NG�����I�c7Z��]���_��/(�X�.?w�������N����v����#7z�_��j�������)��tcr��?��Z���=w�N��~�2�]S�-Yz?��
��������_�k����g��;������-�
w+����_���N��x?�����j���s������=��M��l����������~��V��^�a��n����[v�{^�j�<���.��v�}�,�$�=rkp��?�j���|_�q�_L��=�������_�����]���������%_������S����r�%��]�'�/L�����]�	�e��
�����s�������u���KW�iq������������
�i��W�������x�c�d~l�O�,w1���������@�.�s����?>����Z���T��GBn&!y�z	q?~U	��RK��
���%|E'�Ao��m3��c���;k��x�Z��#)���v/�9�l�=�]���w�v�?6?s��V�����+g<y�I�����uM�7��_|w���g_=~�����o��K�m_3]���if�#��|@D����-<x��A}0t���]�������N^�LN����9�����������������rw�x������������]�DXu��-�ZS�����&�������cn�����|�[	g����x9=���;���N����uM��+O?Xz��,w���k(74f���W��b�����ap�52�`][u�������I�=������q~mC��-�u�����?N=��L|�b([�\��;� H0�gG�?����V�f���us���Cwy���o�����������O���?�m8�o8^l8^n8^m8^o8����%�|k�<���3CA	�|}����[�����Co�M��
�������[���������d�����O��~�$���$���W���IX�.=<�}�b}������^�|v�/�?�_�����3�V��f=(�L������ 6�~E���]����E�@w��O>�������?����^r4q�=w�����f�@�!���������7��$�
m�]\z-[�>L?X�l�Y7�7Y���=}����H���h�by~��������r�#��K��������=YN^n-?�=\�q��'��[[[GNM�.��p�������+����OO��MG���c�#����������j�%?�g��Bz�t����������~�M6��s�%A
�i^������S�aTv����c��{�vY�o�89��.L��o�����''�I�+��Nf?Nw��o����u��[�C�.\�����;�Fc�^_���	���{�p�w�s�_,�8�,N��>��Qs?��G�_�������
xT��ew_x���s{����;���K�o��Co^����K�����>����}��;g������o�%���������w��r�Y\�|������
�3g���[g����
�w��f��Mw���n5	�s\����[�Av����-4��|M'����
�`�<:w��������gL���a6o~����=F
��~<���`��=��x������d~������bz]Gp������������_���������!�T6�/����v��;��Z?5l��=/������:���}��������5���A��o������z�'�X����m�������t�Kx�g��������t����'���R��b��Bo������������4��5z��O���Z;Q(~-��)
��������/w��qj������Za%����n$�B��o��4=�����x]/��+^N��&���U�n�g���_��n��/���[������A��������S���K{�}8���
'�W�]��fW4����!d���3�xun��X��_��e~ Y#��i�q�3�F��p��<7/���r�2G?,��~�JwP��~��/�y��_$��$���;�1��?xBT��S)�u/�V_�I�b��~[Ufx�3�_fnI��i��{�q����JW�+���r������� ����j����n}:0N*����F�q�������x��E~x��e�ot���5du
\.Z�d�^������}�?>: C���x$W��r_������0���ko?4%=�}q-��FS	�&N��9�����]�����<y��`�����o�~�����g��m���?{��F>}�'���]]\G������M�����R]���������r��6�M��8��ej7�6wggh��f����s���|'6O��c�x�F�:k|o`Qc�l���6��{����c�z[zodJ�����lH�����[g�\mD��e�!��5����Khk]\-G���S�~Rn{�v����*������F��� ����E�����lw���q�}�q�5N����peo�|�u�U�w2�k�+�����
��������kbN��y��K�]oL��q�����0�o�o��=q���n�������<t����z�������^P�����DX�+�jd�����/9�� �~|���Cn�zX����l�������c���vpdc��qh�L�V��b����p'�a��s��������X�:�1�C#�n������8��Wv������)�Q|����e��������vV��Op��/��
���,������Y���uk���4��x
�����~�K�tEgb��3��*W��"�������Y��2�o)��%�<{�������[����1J����,��t�����@Dt4;����aS��"'��O����0F�^f+�i���#K.�V�����	C1��-�~�s9�oz%(g[������|���'?���z���BF���i0Tz���~����M]���)��Kx�CxM'~�f��_�1��K�Qc9nr�����G��Y��nR��L�
c�4���
,�C����%�f�o�����O��{�����o�|����:X���p�?���������XZ|G��;_���'_'��S��&�����;��	�tg��y�0w54��uS���������GX�l����q,f�����xH�Y�bj��h71��t�?!�u}�jx�������,�z7_���O��X`�Yg�p�G4��
��'h��Z��'r������S��[}+�Y1�H���L�����<~lD���RU��)�_~9�$g��Fb�c~�]��r���K3�;o>!������	uI��O���~����.#�������������;L��W���+����'���b��Pa�%\�;���X��Pm����6��#����.�0|:Zd�����3�~�������A�x0��� %����"a�^���_�����O���_#�WM��D�&�.����K����o0��Fr?}����m���}%y��7��XCn���T_�u��D9jh=�rt��d�,KE�$!�������'�'Bb�>8�������o~jX��c������p_<x��g����~���w������x��
`_�8��_�Q� W}���
_��Wy1��0?]l{���_�j8z���t��u����
}`i��{����cW_`���K|q��{������������_�����w������i=�p���T����_[�����[�N/n�	���G�C�:���|�l?G�}����3�������}q�������%!BJ��P���f!6���J/�������`���yt������	�J�ir~�z�B(��������h���v0�;9�a�qx�j��z�V��G���F�?�����	>������8DHGG�x�~�9a�nE�3T��_�������W8�h8�
�L�����w�)�~��l�8��	A��{�������?V��D����������h&�FGl�9Q�W��`��u����r>,%2��/�_>x�f��'�D/�g���n��;��__���������c><�����V�u{������w'�O|��e���w�kx�a�k�@�5�0�p����'�5z�k���pl��h����	����2��7�m���F�9^q��\�U�����NOV����������O�>��(����/,������g�����d��t����o����/g���9�:�1Y�N������o�����)���.��]����~�$/�,���>0wh ��1w`v:��+f}q�G#6�ZwW�����o����/^>����N>y�����������������M�����[eG���)��e��\�oO&�O�o���7~��+��+���m6����~�����|��d���������+!k�#�;���a'v�;�U�&'Ds�~gC�O'?[�v7=������z���	�|9��
�q��M��k[=�.������_�o�����'�������mw��5���Q������.	���r1�������dk{�@�a���x�������9k����"�������w������s��}L�q�}<y������8�P�|������K��OB��{[����`���?��l>�{�����e0�y%���Xf���t:���7T�|�p�z���w�=��3K��IIw��:�H����
N�N���!�e�<vv��^�wTp�/P��Z���4��I�G�/�����]����w����1�bg�ro���P"`���a�n6"0D�;$#�����@8���;��RB���������ke�������O���f����y�0}d�pu�^x��t�������o��;I(��}���<��{��i�
��n���t������t��Y�����CF�s�����N>X�~(r>�� ��8��V��kp�p~r��&�p���XI�KT���jB��v�����&w{+��5��t���
.���K�>�^���0�z���>8�ME<4�j�5B���TWo��r������p�wt��4
�q����k����'��v6�����S���ZC���|�>$.�d��;�.���3�_�CW�{� x>Y�Gmyf����5��������1Z���^�x���G/�mk�M�O�ogu�>~���:ok��>p��=:7�\s[sV�6��������������E��3�t������z����;>����d'_�-�������v���Nf��IN�����tw�]�����mw������?�������k�r�N�)M}7h�]=j�}��v���I�MN/��Qrn����U	�d���kc�1?�c�?xO�)�i?���]��S+]���s���?l`#^��������6o��W~�5���-}���D�k�_CB��y!
��ix�u^�����a��J�����r�/���n %~I?U�\��=�b��M�����\����;;[����H��4�c��K��u��8C��������qvI�W(a�us��5��QQ���H/M��P8y=��}V��X�'��g�|g��%pN������K��.�+�=�K��|!�|�;~~=o�|��N�~���*'<]D�c����t����'��Ev�dO�k����&�|yy��&�W��ye�����;Q�k:}�U�����i��W�����������b>��"[aeau�{��y�����	���z>\��h���������=oC�6_�������A���5V��K�������\�e����k7
?�=���5�T�<��-����S����t��Ck�	5._~�{��T���/_�Jh�t*������]���"��~M3��lC�w�^q<
.%����ck&����������e�Y�?Qx���np7��'w$�5)B�5����.e�Z*���"@{
L�v��(���j���Qt�^4����q�u����������!�|��tf�������(����������U�in��K�����E$����"|x���r�`I�lk+X��Iv`���[o&��3u��_7W_�O�N�G�q���������|����~�J3�u-W7��>1����������+M2\��/l�i����E]b�\�}	���\����|!��';������������^2����7���I> P��{������[&C��0wBg�@�m2?7������2{n�����|Y���%;������cJ�`u]����CE��?||m����Nom�S��09�)�*`[��82
he7��39w����C�cL�/�����I�W���	w�;/�B������o^?�����vB��������t��Oy�d�<���������T��_?��!����bi4#�y���&C��^62�?�u}N���M:��M���	*��F�M����F9���9L�^��^�xz����M�4��n��������|�����J�T}�g}���G�?S�%_
%��$�A��2��2�c{a�7�~GZ�/,�w%����[�>�I�;,�a@�������x8�_~�z�x	��}�?���^��	�����sf��p&j]��!N�nV�N7K
���\�D���)C�%�W(-2�;��n����8��<��_����c��l�?w�/�� ��4����f�4��d��<�kF����!����'�\������i7���u����	��0_F�8s���m>m������/���kK�����W!d�[�N�� �M�{�c���7{�����_��o}��q�[=i�,j��.�����r����.F�,X���+�u�F�d�������0��r�G�vZ�_�����v���G�ek������H���x�8i+,:������}k��Ww;b�^�����
��+��D�����_x�%
B����C�oG�����q�Er}���������B����;}�F�l�j���s�.����65&�mq�k��W��(x�R� &[{�\�}�������[��6�`����d���\�z?^������i����#ar+�jC��\Z�����{^��!���b����
��F���
q��!���������P��g�X���-��"��_������t9w��LI���O��p.����]~Q����q�����?��d�~s�3������l�.���G�k�@P�}>c-�`���������N|��o2���o���A2�8�7������3,4im�N���v�bk��FU���-����'L������v(�;���(�c�������9|���O��X�������?w�>�����nE#�Ck�]0Z�B������}�%~�l3��������s��I��w��&���0��m�k3c`2��_1�<����
<"s���v>mP�����[p���o33�������U;d��Y5A�����av��d$���z����#����<;,����+���X�������H��ir�<�O	�k�E�����V��a�[��r��Y��C<�Kz�$��9��r~k�u;��]���a�E���+���� Nk+�^�}Ik'��ooz���s���fKl�\WH���	���cVk';�����kJ$�(o�� ��T���J�m�����{�F'7|���AW���w����\�uo�1�r���t~����Va2�2��~rN�2����,*��=��>�8M������	u�Am�|����k���)H������v=������v{�qz�6����h�8�r�����n�e]������|��[����������TR�'�h�~��\������#z$�vv���s�����;����CE���Kp���^B�+��qm�n�3`k�`�q���j���3��O~����d;y�k�.m�7@a'u��,���Sg48��AWK&J~g��nx����>L>z�]G��-�{�4����ru/yqc�5���w#7�o������)�,�u<y���g���w=u6�@DU��o�jd��'���wV����p��r���BS��]������FW=�dT{�74��3�{��&���5Q��*����(��/�1
�\!�/��������}/��q�+_p<��V}�Oe�����%���	�	kl���?�e,#^�y��T�i��K�d�`k+u���;_�����q��n��is�u�|�����{�u�/����~�v���k6������/e�����~�W��O�-GV�^q+��MF�K���\)�/��n��$>k(z�W��a;���Qc���F�A�>Qxo$���7v�e9�%L��������[Y����C�fXOm����w�������E�ywMo[�jb�W��5�$L���b��9i^���P����W\
��t��$����������Qw�����#�W0<5BP���P��p���NG�?�)Wa�a��K�����}S�S��j��x����T���3h<l��x�&��_��_�O�a[���������O���_���I���6���4�*�=��lXb3q��T�!������.�pI�u�%Y��7��X�X�A��I~7�z�	S�"��?�k-�a�2�����w������B��3�f�����sV�"��GyvB��������k�?�����|q�����!�X����(E�Z6�y^�C^�#ly�R�N�dR.N��]�o���Z�wCl����{���fy���;�`��K����G~�t�����j���{Z���������I���w
�u�e��e��w�t�(�C;�_�����o����C!K�A�h���TL �OO/>��r��c���&�����d�l9����������Q� ����/��C�I���Y�`9����W�>|��(���a��'�>W^�@W�����a������;k�����k�Qg�����Y���EW|����>/�/c���0hn���&�K�v��\���d�M:%|�8I�k���$>c���������]��!�C�k��'y���v�U���G_�iI����|������)k����t�T�go�t��YO����eB�U��1�Ds��s���������.owS���_A���b9_�/#D]�`���t�X�n]�a_���=�6_yk��/����~'��F��Sk��A��7�y�F���VI����W�K����">~�����b���%����Uk���c����?�m�D�
���sx�Z��9�g4���ds���K��q4���W���k��>7��K�=�q7����������5�^���7���"Ii�a���M����3��������o�uGp5gq��
��j�V
d�&YW�����Bn��
1�H���k������}�4�q�8L���~ER��~nJ��b��d�����'���_�p#�h�/��D|�GR��������'�k�=����(�N��|���M��|E�*�c��]���y�`�n$q��o���L�w�kKp^��>>N��F[[��o�sG��M������H4���H��Y��+�� �zChi�C���^}��j������l@�=?������{�b0 �Rx��
Y��x|���������}��V?��������quf�g�{\��a�
��oF�p8�u�V�4��e�u$�
$|us���]B������:����}9X}���������A"
Y={Y��6�QO����F��ZtnY��\��������q��)Y��w�o��e��I��
����W����;������/���B�3�6�1~i��L�������A���S����h7O;-��
�~u]�d���Q���$q�6��,k�u��j\U�r�1�N�~z���zQ,F]<����Rm�hC�3�1x�6�UY?��6>�I�
4����:)�*��u���\y% ���06�������q
&s���V�
��_��%���M�W��M��&bn�D�W��k���h������da�]Q-�_���A�t�������NN>���p~z����U��{����/A��G ���B`3t%UQ�y6��6/���<<�z����@��) 8(;WA���5�z�����h~~��9<������8�����L��g��6��oyw�
�c|��b9>�����q:�.*��<���{]�X�r5�����:Y����j�:�8=�m�B��v�
P�/�!�����Y����x������k�:!2��TD����������@��������K��W�e)�����66��y�I���H\<sM�o:9z[L������u�����UBS�O�����
��l������j��(��$]�n�}���?���j��m�U��y�oU�g������5M8[4m8Z���C����}�����.������Z�uU��eko��ryk���}��6���yi���p</�����y�o���Bs�{���.
?������"��=~���>�;���]����'�������
���S�����R���	��q�Qq8tCf��U��3����������w��um��}���j���
?�"
����^[
WYk���Te����}Ga\6 Y��g�Aum�_Tt|�q)]O�/���-l 2��2q��x��mm��=��������)������d��_pYEG�����UH����:\����U�\���njV��
����B7���|�^7{k�Y4u�./m
���~��,����4�J^�u���I��4���z�-�W�����+��	@S�t�=�L�>��}��6+�5u���a�j���9�����V��&nJ� f�{��D�1&���deR��i?�)c\��kM����2(3����'sx���Y:���(;�^� ���	��M�tv�j�6���N-�w6���`]�K��Gk��2��ue^Y#5��!x3W���Tar[8�H{]c����0�j%z�	���q�M�
q�L�T�������p�k����v��J�+��_��g��nP���������q��OB�j��Tj{N[5<'3e��5m��r�5�Nn=��a
}��x��Mj���\��^m]Z��4�������1��K���
l�WHa8����^H���J���ubxje�����������S�x[>������&/s4�-$UV�x3S	���Ya+Z���T�IS2	l.��Ec��%5H\��U��l�Y��F�*Qq�������+�u��$�4z�*���%��%��P�J�&N��Lf��mCO�*�^��L8]��1%�R���k������bu�����jR�tu��M����$.��u��Z��0�PH~��$�Z�cNS��'mc����0���A_&<}������U�1�}]��2�����8(��$��
��
kS--�D�6Q�[V��:��u�"0�Z��6��������-j��~��2�6�c����\�XP5�h��T����%�+�>o�;[�G��������%`#m���m�������u�e�-���Z��y�L?�=��X^j�k�"5�_��,��-"
��tA��7���D�������M�g�-���K��2[�������]��A�wlnF|�b�c��-S�lYdT�����`.v����-K�*[��h
��I�o��f(Z)�_��3g���&��������e3�m���l\w�e��������v�8E�k�6��
��O����^���(�����j��Yf���-�	�"3���C���D�+Pb�l
S���0W����yYg9�l]���E��������lnV:��Z�d��6�-�1���d����eLk[V�"c�����@��%�Pks�[����TM��a���Q�M�2DW�e=��4J�2�+��+>:c1��^`z�$J�S�H�����M�0%J[�Z�B:�:���������P���X�Rfp���h�S��c�]����,Z���c���:�fp�:-i���05Z�4-"�1��V����*,Y%S���ne��������w�A����G��7����;�e��a��ba��22ums;�gi����wq5�x�!����-;M��l�t��x�
����"n;[|����;�7V�����:l*)n��������`1j���U��9�t���W@3_����m��c\�@eh����l�������"����jCCtcQ��1�iP��/7o�:��N�����jr�846|Y�(�����M������Tid&�|L��f*x	��:��e8#Z��<cy��
�QJk-�%�!���9���X% �S��1}[���2����tW���L�����k�	1����`����::�F��TU��m�b0s<�HB�s������
�����:0���Mj2!],��Q�6|i>0�Bj��k�7{�&E��(�L3���,R$�e	[���3��y��O����G�HXpE+5D�x�(tl�2�v���e��D���5��Q$]Q$���i�� 4��L������2�>d�~�,��"��E	c���m�������$/5�j,Q3=�������KX�X�Kp0-hB�������eJ�+&�����oh>��>���d%o/�E�D��E`	� ���iQ6�F��)�W��1"����%����M$���>����?3O��U��
�D'HY�FF�)�iV:����KJ�ZX��/���e�
�1Q�*\����&�M���l��|*�5bVah5�xY9����vA��*1�s���#�2����	:���h��$�C�[!y�d{�V6;��4�X@��Y���=�� ��2�x.���F����`�<B�CGh�e����yM5ZU�j�kvm�K�a��`��Xy1Z��*Z��;��`��<��:��P��:*q�Z\��0���s��Y�:����^��%CZx��
��"p�LY�L�*�~h[�N:���*|�?a4�3�q�hy��|&�q���,�������*�9�`���'��J��Lv���a�PX���
���,���?[���W@V��
N_
�����������:��\�t�^�@R�*���l����o�U�q:I�����l��%�����8��b�d3����+%}������m��
"R���,w&O-�v�9�az*��_0�@i-���h����q&<�1&R�T���I�T����Tu����0e�mX.���na8R��m[����%���
�T��kY�Nj�m�|*�����<����������W�Rm��b���g���������������
4�LF(������x�z����,/��yKF��b����RV��k�lN,�Ha�`����"��]���`���T��{"�U��X�g[�y�V+r��^�(VM��h@�V��#����qM�$G��Bft�bc2�Ab]n���?G�j����9aJ����_+��*b����@�3V�m�1��Dw��
���)�����Q>q��s0O�m��
[V�D\o�G�&Hr�;I���d�����^�M������7:��sWll�f�	�5����UhP����j�(�vX��i�"9�
v����I���f�!B��q�j<��K^��g��F���g��L�/���� �:Q�����f`
x�t�E�n
�����
 �~^�B]�%��xB���^zlE�L����u�������������e�`�tZ@c�^B�1?k`Y��XF�F9�[~`R�vsU�C)��b�aq���i	��	�8����������vN�����EQ�����O�,�`S�2s��kP��������5e6���]C�"{$(�H��A� �x����x��#�.���}����	��`�!2UV�06q%R-Q��Fr��N7��dw�H�Y�
���5��A�0�rD?�Y�qa#w��~�1c�@��*�ak,	-�9��PA����v�o��(���;�^hX;1� �T�?�	���j�H/�6MS��m����&N]G��	>��7_0s���"�_\U�,
�Q�����n�P"����������[��#��*�
J0������x�-�^���Z�Oy�y��bP)�1kDcy�l�0]�D���H��VL�t����`_����	�!��u�Y6�,F�pY�(�*��#����8��!�oK�1���bO��������p�Z�g|��q���������a2��S�1$l���5@�{��ytj�	�+�����H�~r����dj��0z;��gx9U�d,�&�� 1��pRXwu/�Rt�E�)Gp�����;8�����j	�+A�O�hd��^G�����p���=�.�PXh+& ^VN��;���E�A�)y�
#���N����uX/�������
2�'m	A�5���]��p��z�l-��:�����&�Q��x�g>��4�����h�v?B|�ld/	�W��:�@|�H��8��No��mQ�P��ooV��1@�m���L��v}������1<6V���n
u2/�iF��
�	��1�n���U�@e�a`Z19k,�ZP#�����f��o��r&c���%�
����QW��S@�.I	U�8��R5m��H�n���n��.e�N�$�?p��pC����==����'�x1����HH3��DMt��o�l��[Bz,K�F����x��@f�5�@��
&�����_o6)��T#h�1���,��D������(�}Z��%�X
���
�4�p��:O������LY���#�����Y��+��3�{N��V�V���H�!��-A,���$�P$@��m���x~)��R�KU���5V9�-��.N���e�-����"2T�{��q�)�y�.�&|	�B�`8��%��Ri�-g
�Ty�������=�V��l35��R��E�o��+Y$l�xpL���;�X�b�y����I�+2��������F�-us�a �3���2�o�F���8!�&9!����r��R�U�p���P�0�
b����7���1.�_���h��Q�D��A_���C���o�;����^@W*P�������2�����\ePf2��.�X��*F8�h�,(���j�%��7FhbL�".�y)n-ne)Q�����o�?1�LL�2�4�x�!����xB���[f)!M��Y��'��P,^^._�
��
�����V�HVo[	1t�Cb.�����<��LdB��;�Nj	/Q�X�%�YKO���Q�����V��V@�-����n
��V������%�WV`s@��*.D�������U��n�����n��s:%�o�D����, ��P���}-^P�������jjF�-�t"���
����� ���2zC������$;
�{���dUq�6*V�����7���#���$Hh6.+Q�����u�-{��-D�Q��V��=�hM�U���1��c7� ��w!
`uKJ��n�$���C�mdd��Q�B00���LA
!g����:'%��T�./���G�d���,(b�$��J1P%1���(�(?��Fqu|"��G���K�d���Vr{������@��1	�����'u�$���3s!���(%���Z��������"A(*���i����:;\����c���[fnI@+T�g�YH����P����,\U&���3�D��@K"e4�BU��O-��89����P�48����a!�Bm�L���`��
�����L<Q<��-���L�?%\k�1#W� ��f�"l�>��TD�\I|�����d8tP�>���#|m��R�y�"��u{��9g���B��8�p@�W�+������{_�f#�Z
�,�5ED��
�WW���8�B�:�Lgp�Z\�u�7z&u�m�Dl�*H��9Y�Z�%��Q9 ����X@������V��������,��j��QW����H���@����0F*F�RA��,�3�c�1���6��)%��D��p����>���ZB�d�?8> c!R��������g��J"f"q��U^������x���RS�gF����)��%���)�9����~(A�>��z
������Ba4A&�H�75��c[(���Rh�������/x�p��lTJH�7_`x����8#
����@��4��!�mD�Ca��WH�l�.���%���P���}����h���
O��"�����!�v�#"!9��T���So}#����e�)b���mol)���:�n��n������>�f8�8%,YJ��Z�8B`Tiq��Rg\���Rta����"��j6@}a���^�)!��\YU�h�qU|�����%��R�<�����d�_����by�+P�������:'_���K����i#,D(���`��B��	�h�y�5C�������EJ��[�z(����/�����Q���[9�����B|H�!�1���8�I&>�=�`i�"J�E�.�*�n�E�WXd������[H�hM���.��4r�k�����#fo)�T�����P�!����P�"2t��{$3�	�D�0�Dy�*�G������%w��7En`*�7�8Y<T�/�m���c3���-V2X�R���G}�h��1�:0g���se���|�0��&�����3_4���&s���V'K��HV���WJ`%��6�Qf�
�����	zAgf��Q���W�P+���r-T�� .0�PG�f����2�
�mp��
bQ��1�:�k�*)�Z�
I�%E��(<��^����f��/��5�:�a`��Q(�OEY�N(�S'��Z,:Qb�[k�D��jGlN\���_-�0;Yc ��rWiG
��=�m*�Z�����(�Nz��'��Cd�V�?�X�F�t�ze�#6c�G�j��Y�@2�����������EM���e������2|�2@@����HCB�AP��T��P2�qK�T�� 	*����
 fIJh��k`E��OV}�:+<&E���HK��</F��O����"�W5�V��>��Bq8y)�����-��.��� ��izG\s��� ���Z�s��KL����:�z�;c���/��A���@�Il([�CX��%����9������L��������
�_��/-�>�,�A�[�0-���g��H)1Q��6��������/;��������Ye`��k�jU�"��I�*s�����2!�FErX��%lAq��TaI0}��x�,�
D�<W
���jj���M�BM����u@\`j�;Gv�'���@-<��2rw����V�'U�B-q:����0�����I,�T�0T�#�A��������,S=�c�$<�=V�Zy1���|#r���V7x%T"|����>m���3�Z��Wqe���JM����\�)1�����"�����R�
NmK�Po�w�n�\�jT�������W9Q�*��c]9��%�b�I�<f���Z�Bk�iFT��	lD��R����e����y�U�r�a�U�i��5�*v��������%G���U6�R�)���D�Va�2f��a�FVH��\�K�Z�F)��.R�yW�8�
�j
��^&4*�CM(K��T)����v��"r�a��3��kY�K@���H2�L%�h��Zi��R�'z�8a���s2%ED�8uO����!��kx��B�UHTj��.
�P������8����k��"}�������;-L������&b�Z��^r�kp��XbM
�r�K��.&�1�o�y�����N5��J
W�D�.DkiH��T%)X#e|�*�^�d�{�A��4}��b�h��*���F��jC�`�lW����)�\i��"S!4(����YD`�����1;:��BUKI���l�,*v��J��tp�x=A|��[J�W@��R�����Q��Rz(5Tq��Ru!U��8�	��Q��
`(��� ��)�$)T��PV�@� )�*�fT�����@���%5�r*�Z�4.�QJ7�&s��7k
E�P���S����@7)�S9��2y����L�S(�0��P6Q�L�n	t0������������,�$�t(zo�VdD@���D�.cH�- ����6r�1ja{u�o���j�����1y�N�w��/3��!��EJD*���S�����P�}o{�Q9���2�]D�����������\0�<z�p�Q�9dq����(��R��F<�>��@i%f�]�2��z^�p��D~DF�+��0������J��hd��'�P�Z�()D4n��d�B$T��{:��C���Q)5����R�m+w���E"[�5�MT��f��*I	)";<�!^[������ MG�df7e������/�[U��[��E��#Pj��z%�Y�_8M�0�<�`X��q�k�[��
�AC����A��%��x��s]�rc&8r�To�l5O	>�Bw������=U*�������1�W(�:�Y%��	L-#�����?@\������j��<u�
L��@R
��@6����l���4t�	Q,em!�:�A=/�Q�z��F�qY�*(�����
`h�=x�>e&~�J
������\����L�4q)�h[=����D+Ub�0z��p�w�@@���V�������*%�Bz2���e:�7�������e��
z+2-N��������(����R-�Du"�
^)�[�-�y$
�W������b"�@c��|���5n���s��$�X�j��
�����c�I���/:Q���8����
A��U����������WB��pdT9�	H(K�\4���@�Q��uq2��c����Wb��#���(�2 ��$���(V��������as
���c/U$�C�+P�W����#_�X�reSi���#�I��7a�
?���*����&��a>�0�\��W�����0�ci�L�`wkQ�=V@�
w�(p8����^	��Q��7`���@�T�D��^��e�x�J�+�����������Rd����hHp(Z
��-E�Uh�Rz.���q��Ec������r�*
�����w#�D?��W�����$����gd*~��G������<h��i�+�l)������h
!Kr3(0�"�����j��T.�b0���E�>h�[E$H�P��n��j��M�+�)4�5	���2g���}*FV����-�J�V�<�u��v*)�����$���j�����7aa#���1�D���U��5��*���j��Z��BTr�$������J����B�E���k����f!�g�g��u��Y�f^���=ty���Y[U�Sl��0`V�*��:��b^ �/2�(�P�p2I��J(9���^��r�Kk�������Eq��?�|7p@�	�h�jV,�_"�(����5��R������Q�R���r���Z�5��H(�x�+*K��[-��Hn�cO��h��Z!���:w�l�����`/� O���
�5�&/2�3t���g��9������h������(u�R~u]���=WXY�!Z2���nm�#���V%����hD7�3������X0���<+I%�_�,R�d�c��JO���]��p��H�
��M�*�B)=J�b�T���P1����X)1a�VI~`���D�G��:0���h�cT�kM|A���6� b���x��g�2�"�t�������<�Tv�|M���f���
f�[�r��ASFBEJ@��u	v�����k%Sz��VKl^�e-1'��M�q0�eUe�Q��D.[||U)�u�����(�������f���j������Yi'�>+QtE�!�5�������	�F�J)
��RD����A�S�Lq��f
�d���'[�)t����$��I�4����@Q��\�h�FE�}f����g�FIK)j-�NI)���
��y�RE�:U����
�����m��/�QJ~Fl:��E�T��c����
���+=�52�$:�����4W�U(�bx��
���j��P��U�rD��&����R�/�4J�W�:QM�%K��j%c��@�6r���>Bd6�r�Y���*}�X��BD9�_�*@D� X)�)����	Tfb���DbN�Z5�����hP��+�lGlF���d�LQ;�
o0Mk��D�U�?�"�wR�=2hA9l.��N1V����^����=�p�J�59��� "�	$bS���F�K�
s��=��Q�fo���}{�d�U���!Z�R�+@�5�ho+�1��V;��_k�X���:��,&�!G��Vu�e��w���S���0_��`tm�&���)E�W���������K*3�4���5Zm
����Re[m��b�dok[U������Z�V�K�"�#A�����2�`�TV�V$t��Z	J"�l6����&X��6���@-aiqD����6��JQ��&�������;(�e$�~[����������~5�\1j��T����@Y���2P��_!aE��Dk��T�ld�xO D#f���a�z;�
��v�0�����DJ6)���[-S�x��	`�5�6P�-�u��lZ���ua���KV��Q�����<9�8�z�j��6�m���4D��*aV��`Jk_4�?���0X�`��h{�;������!�W�XiM�
)�����F�E�������D�� S���
:�	7�����i��c����d�7
ST���Z�xX������H�3TTu�����v��W������Q[���L*�*[U;��_��e�kU��%��f
g����E��J����3�-�
����x��n������T�>���&����'�E�)���>���:-zLB�P*Y��f�����M�T�X��� ��
U�^[*�����3`���Sx����B�lD��m�Qe�
��Nb��V�`�7S�T�/W��J	`d������U�Q�&���J�
c��P�Aw��s�x����lq)�,��`�b^��U�I	���t��U���j_�*�e
G`�Ga�����T]�������~M�3��i:g��k������c��2���Q���PyVa�����Z����I]�/m2�i�S�,>����,����ET��R���.�8B�$@{��k���b��T�0UZ;��4����Iz���������
Q��F�0��{�7�6�(Ba�
Ff@>�J[��P����i����g������U�M�>���q�X�����_�<M����wbU��Y�?jze���R��D�U�	�	n��@x�R{3����
17~����}e` ��U�O{���&%`�-��dHw��cUQnV�-r3��9��**��JId+���cx�s���X���������i5VCw����bO��<����T�m����b*��n�0nt�����F%��!�:���	8�������K�IB%����R=>�T���v�bHc<���V,���V�&�t%zzM��lkXk��t����Z����X��Je�R~�Q�.]�TD����0U�R,�B�Z���,*U��"r�*�T�F9�y3��yP��FRA	0�	0T���M��\t`��5�����Ef����68k�P�^8�h����#=�y����Z{;��,�b��Z�F9
������'W�v�d�*�����f ��_�l�v|�M��;RIv U�s�$�����X�,lT
�_����p��������b6u����h�+e�����G{���l7Q1����/_&h����I�& V�:�B����m�y=U�"
RC�*q��T��IJwlU��J���������3T�C����bg�_U��$w���K�zwDv�b�n;���,M����	X�*p������M�k����)�(G���Z��7}�t�M�S+�Q�o�q�E;m0z+P7@{�;��B����r��-�����<�hB�*�lTu�
U���&���_�
EM�CU�<����X
P��W�R��Q�tHUXV������a��=/IR:s^�{��&���������*F����G"u������e��Q����j��E�js+���>�&Fn3�9���M�
T�(�J�(_�b2RZ�%#4��M��se�S�N��u �5tK���4�E�
I�W�����<S�h�eL):��p{1�U%O�j��BhN�T�7���=����k���9���
9K������u�T���h�ZIQ���V��P�����v�vJ:�������]L��(}U%�Y����"����)����"�ta��di��"��U ��$Mh@E&T�3���)@�V�������>�;��])��|�R����E������(���L�P*�� �l"VFf��+��"'bBe�C���X����(0Lb���rb-��"��U8��W,cl)�lQ�*8AJE�j��N(\�Z���41e���P�R��"$�f�z���I�StN����@0���A�u#r��vl��w�%k=�J/UA�����5�A���5q��TX��.�sQ���bdj���%.���n4�M�j9�fx�J����]����pR������wAfx�����b�+1��.JJ`"���l������l^2�l�#� ��I�u���T�B%�l"��xd9E��������2�F�IF���s%�v��D�^��Y(
D�wF�S�<���&��

��@��&�JEvb�j[s��HT�O��S�����xi�� Am+�?'�KU��Qh�����$4h��DEmre#���r0j���R(u��r�X<Yl���7Dq�� p���
�k;2y��3"^%t���61���"��RnB�(;E"�kg\pB��,�5T���|�4�o�^3W`���*�����&m�������B�����(fw�?�#fH��M���6S�T�2�
����dH��S��j�AV=��j���T@B�����L�	�M�	)!B�S�R��
�B��5�X����m-��W)�T�^2���
(��W��~g`��������Q�)s:��Ch)�@i�:f�T%2U5(��(a��jh�SMc��F�U+��J�w��2+e�J�P�+������z_u|#��
%��GEF
u�FE��L~�2�TKM����P���n������n� ����W��7���Vu$d���q�����(�����NU��/�e��a��Xf6T
�\�WWi,��3U�$��`+�P���2,��_[�d a-�3=������
	���i�����L����:��U���W*o���UnL$UrP����0�b}2l!Td�����f��'��6�	��a&�7Uo�����)?P���iO�-��|K��Y�uR����7G�@u;��i��T!Og6���G��P��dF0�FU*�K�=/��3�Yr��	6+
��j�R�61�$�'�{N%���Eh[8Y�R7*)��L]�6�
UEYAL�K�0ba!��n���=H�Q�wB�L��H�������z��q`���E�*G<P,!8���-�=f�*n�!����l��*�
fJJQ���:����*��c�'E��8S���g_n9n��5���
�t������+h��y�"*��q��������fp}Wh;5D���D�:���Y����Rd�����������}��^���vIS�B8U�A��&.�`4����� V5�B7
��!fi-X��:9�J�,J�x�@��J������������jHL��Od�t��9��R��L��a���VZCI����o��P�Jms <��"�E�o%�t�j���������YoW��J�c�V9�
��\q�,���
&�`HRv*����&;�d��^p�DT���v���*���&
�"@���I\��+[R�!C��2X�5I-����� \��f��}�����������SA�A���o���k�t������Z�W�=�.�Oel�O�����5��A�
����D�(��
���B���bQ���#*�w
f�������E,a��~iRm���*[a���6Cs$�Y���iXs[n����$
%�2��.��(�A��2�_R�L�����d�G�H?�L*���"�a�W�	��TsGZ������WR�~�|@��������cG����'�����l,n���=������&g�8��-c��e�����J��2B��>;�A�����S���uU�VO3��3�
�P�;�����QU�E(�N]�����m1!�[T
iM����n�?��������\e�?W�{y���B	1���~�5]�]$�PFR��~�>��(����U^�6���DA���B%���K������������Xs3�-W�~����5���&5�r�+9�5�^�*��1���������1����z8iJ��`���G
XC���*e���R�00����U��]9x��D�T�@�(G��v	��V�u�g�4�M������
(z�o����vsYt��i��������b~���b����}��Z�m"X���PJ�`P2`�
CT5f��I-Z����&�G���+�,`�C�j�s������R� ���X��.�hkE����m�J~V"���Z	'�<�`.R�x�*�
�,a%�=�>c�	1�����&'��T�Ra�&�sfv"J|_���H?��}�P�_�V�C�����t����o���0c�.e���P5�U�<1JT{	�����w��,�r��*���6#]���F��T�FK����*��
n��a	Tp	����oO-�V��DB�F�?
����KO�S���K�iY� ��i��V�A��1AL�%��#�/�TxY���O�<����aH���kE+p�����S��R��2N���f��
c�kK\��W^(��-��Z%4��R��N�u�K�I��J� n��#Z����&�K;+;K�)���=����L	��6��
[��>V�������'3z�S����;[LY<s�<������h��*+`��K��;P(�;pb���\C� �T�gm����Y�Z|�NIM����[,��P|Q�t����M���`u���[TA[1���p�*�����i��-���*�� A��	�7d�5��J�������B3,d*X�������P5���o.���Qxoe�W�2#��M�&�l��i��*��u�M���$f��}M��(J6`���M�>;>zF��F���%�U���j�����X��u�9O�)`����$�n�)_aRfB �(��W��v��X��$61��X�
�^�k�\51{�����	6�&X��@��,�&�W�t�����P�j^i�V��,6�oX���Q�����x=��9Y�2��eZ���/���V{:L�@���"y�
����O�P+
�*M�Q��D������Ux(�� ��6*(�1(����u$����TH��i2�-�F%��9�C����"I�%������_��P��B���f� &�P��SR�
/^J�h#�0h�T[��� f�"��[QZ�ld*�H�/�W(�(�5�`W�����T!��"�X�1�f\B\�O���S%���d<ID��V*U��2��Fq j|@�)�u�
�Qf����F�)����@=��*��]��3�2�TS1�xa���(g���x�4��k�"V��H5��:;W��H�m�i!n�v>�=�n����Z��qDN��Fqi�>BN�@�y�Gc!�q���02!���i**����*q;t!�\3�8���@��k��J^f��|sU�#���t��Q�F�IEh@%li���"����O�R�#Y�lbR+{�9?H�3�%��M(��� ���JT"��<�)V�8�{#�8Lg����:�yo�us�������Q�d'��RNZK�z+�JC�����'<�3�G���a���E� LW�L��R8M�-z��� �r:&�*�5��)�������4���aR��,Hno��Z��	��V�����(��vu)$�zmJ��u���U���I]6������<�[m�����fT��c\�v�
rm���&�:]��KUX�K5pP��ij�|�:�JyW�28fN��
�R1	x@�ga_%�J<�Nu���4��u�"N�!�s\�Q�nQIM�J��0��K#3����\��v'��M�;oF<T�Y%����@�N����m����6Lv#5l�z1Ec�<��:�*�l���C�.X�� �V���
��5��Y���<����B�����D��N�0~lKY����s��������W��S��v�
Pd: ��.�0(r)�HE\'���N������W�L��Y��twU�����8��@O]��x��	�����b��s��z����e�1S�U�|���O�4)�H
t��#�9f|g����ER,�j`$z����8vUXY4q�_�y�
�����ti+o��G������chf6&3�����W]`�r����Vi��n�t�z�i��J]74:Y�.�[���a�I!�Eh�2�p;�W/����X_����bN_7��d�T]�6��u�ag�s��!S�������4�����zY����f���������<�l�M*���F�i-V����������lUK�OY�U�\�
jSSy�|7|]I7���9��&AN�[E�,�Wr@UZ�����L|A�7��I�U�j����)���
::(c��(u'|��#OQ��I��{U�k'����:��7GS)���N8�d
�	Cb�x/g���~4�`�5�i���\(��	�,8r0J����`.���,�5wg��V����r��W��-q��m�2����
�H���zF�P����q�iP8���u�}�������ep��w�[������B)�0\e<��Ad�<��K�Zk�T&e����m�������g:�'�H�l*g���PK��
M��1�9so���w���/���W+8���uMGe�z���Q�1yz�s��Ox����A�X�Q��I�C�0::JXCq��l��Q-�������n��.�t�h<��=�q9�����[��vvZ�Y�:X0F�R)q!Kd��o�0��Jw|����
�U�&��F�S
[�f��������cI�x�];J������u�
M�+l�+��J����
$��0QZ9����H���X�{ag{����^&���%m�� S�6dU&�Vp�o
��I�O�rX��p������:N��aMC
��R�����qY�������5��B��s��+�r���{\t�d�o��%�&U���0���t����������n�,��������y ^�������/pv�O��b�n��X��.6���8h���\����{���C���[�z�A+�T��h����Xa�!�4���u�������O_O�s�y�������h�Y7���]���s=�ZU�P�QKz��N���f<a��w��E�vX�Y	,��r���t
3�:�D�F*���X9��Y�����^aPaxl��r����_����i,4Hv3}^������|M/*����+E�I��J�&���Z )s�VV�o,{#B�<�j���3��{�9���D����WJ��
�{�,�Y������"|wx��>v����z��� ����,*�2�'n�j�������*9y���>�z1�
�N��R��R�'Z�5�Z�Bi��bh�q%@�~�t�������96`�dMn]���1�
�fa���u�K�k��]�����?7s�
�wd-5��zu"o��G��#VG���N������6J�vB��1�6�	�O�`����s�1z��VDU�@z"����+�N���{�	Pt&nUo��$�k)������`���������W����e���Q*n��fTf%d������3vu�|������<[����`����8-$�V|hGlJTU��9��=���uRJ�F9��&B������ M�6���O3"[r�t�4`Ol+���"����I���W�#7�S0k�����s��]�����25���8��A	g9C������U��xG������A7W�M��n5Mh(��w.�E�J.�}U�%�f/��i��E��N�S� ��:}��UY�b+'i)������x�1��D��q��!&g��[q?�AnE`�A��u=o�F[9,sX������������jt�,,<+Pp�����HR3��~t�5�|�GoL����F����������,������N��C��xb�����h�Na�6�hC������@�#��K/89u����:����{��
R�\7�J�h�������-�T�a'�5���f�V�_��F��Z�nl9�:#[��y�Q`�8F-�.Gj.,s��+�Qd����E���T�pB�c�fzj���dR�!8-1�Y������g�%��R�u6�z"�^��H�A�Qbd���H{������+�Q> �5����^]��{��P��;0�H�v��b�����}H�w�4��}�^R_��e�)����Cw��C��	�d��T�a:SE4���u"���]_c
�iFs�_h�����p�������A��9��
�U1h��
�RD`Y��P�T;���������1��"�K�x�Y���#��s�r)oC��Nfb5�P����)h�&��4U�z%{���"� j�d�.L��[|m.fdBft�������Q��V��+J�#�}7�$)t�J��J=����PP�b�v9ew�9s[>����xjQ n�����5�%^:m����\��/P��x!'%}z6[�H����'g���Q�#������F�*�b�v���C���j�`�0ZL���I��Anq�-Xw� $sd� �Ti�d�����4�c�e�Kf$V#�HU0%�	��0Xk:I���|��>���A�������!��h�KpTi�gWO�^���v}
[yP���%�+��O����\����`2�����dj�����m��UWiUv�A`,YA�l�T�b3V�
)�v�=��u$NW[����?���������g�;SO�m�4���O,�ed)jL�L:���g��#�%/��x�CD���F���C��,L?��E\m�m��Cw)���x_E'���
�!=d����o�\�R�]�$p� ��}����&�Q��s(�[7<��q�����^���B���Q���3��q|����l�e�"�2c�"�d�pu\�<�����a[U��M���)`����h����uC���+`hEi��\����n��������,�uR�r)��BDL*Pw�jP��o	�v�S�_���.�� Q���:��3��0����.5C�0���[�3��_�/Ku$*U-��l��S��`��M;~�PD��Y[��\�Fw�#Tt!��+�r���=��K�����9,z��j�k}�i0��	0�vVEK�%aC?������s��&��=��P������x���h�+��XR����I����J��_���7�h�I����������&�^n��a5��]8���t`�oz3�K��t$���yo�y��H����f����Yxg���I`V�J�/��5[��Q���:���fYy+�����1���i	��"wU_<���[@�P��g�5m����n�N/�U��g���U@�q���l��v���X�ReI�B�aJ�-��6F ����2�T/���Ih,U��iMj��:��t(M�WfI]���}��Um���5��F�Qi��d�'��$N����v���cT7�i8}lZ�j:n��
r�.��j\e��w�Sh���b�D�4M��&��p�O���T}@�ft(�%K���3k�����
���(�2
dv��x��}+Mk����?f^\���M <�o�)��1q�BnFs��z���|�r������{��}a;��p4��^��V�]h8��&a�uG�W��,E	�Fnt���S8��2���05��^cC0�6�'�#�&�q���o����L~�<d�NH�h��`qB�`Oi1������r�)�h��]��Ay�-�Nz����w�����������-
����A2_�'�0����=_��SG%Y��)J�lB���r���y@���0�����$7j[K��'�V-,��:�/bW0f��-�}����.���k�=f�R�%-B#�q���oS\T]*=i!�y�t����'"G���4zd#�����s4��%k��pH����{�������;�,��v_zx�V�4b[����_<�(�.;����8L��Sx�����<)u�r��}���@��;'y�<�m���H�1(2Q�48��l��JC<����0W������T%�����;���t��4�!�&i�1�f�w�W�/��jXMO&y4�N���
!a���F�������/0�v�'�*��F5\����Fd���{��R������F�5w)�kC�]����
=`���@S��n���8��Vy��L�1V���Q���r&��M�(&���^��	�]�M����-w"u��%AK`���1����C��
�^��-��l�=@L�t�dh�@�MmY��������������$6���f��=88��c��2����G�������-�?�
�wud������i�Pkg`�����
_{a���hWl3<���UL�I��&�U�����.�A���������v�p<g��"HAF��r��h`Q�4��F��c3����_�TN�.�,S�F��3�f���d�?�����0G����K���$a1��H���Y�n;��$���X�
6H��$vkh���x����n���D#`�b7c�<��s�I.F��P
p��b���V�TtJ�<��Na��P�;
���/��SU����D�
%�0�1Dy����4H9�y��QL�7�@A�!wrD�����1a6��[�d-+��~"k���"�>s�2���D��x7����x/�"��7s_{S��V���C&�^j�"0��[���\V|����=���t)�&���7]!�M���U�6V�`DPh�F�]�L�4�c�9Vg���� ��+:#�B]hu��{;[`�����Z�����1%|�'�t�M�1�9�b�:����m9l���q��q����C����G��l$�K����FA���}dr��L;������m��F���3iNi���/��~�WM���N%�J/�s���������+��:5�a��:���	+yh��WO��}j�����,f8DV�f���	^a���K�$��C�F�3����3R�2�uP��g���]S6q�� �]RL��jvs�i]eQW1;
J�P���VY�����
<�2Y�)�[��m��o�!����\�����tx=�v�v��L����,�t�b����C�o����I���zL8�sZ�����@y�7t�j=��1���_>ta`���[�*�Z����6D#[��P�5�����y�K�3�I�m3!�AU���P�l@�FF�0o��!a0I�w)[�]gw�%P���9�6��]��2�UA��#wr����IVQ���lk���t�085k,i��K�������<��	A�d�[5����/'�`��Q������F<t�����)O;c~�\�9:�(���I/'����������.I�(�� ����1���"X��l$5Hp���*����o�EGV=V��"��>1��2����
�B��f�����i�VG)�����l�h,4z�R�aJ�M���PV��]�-LM�>�MH�5D����>Y����280��/��8��d3�APN���?��(/XCw����yD��\�L���n�U����'�Eu	ksy7]��b23$�*��US
�]�U�J��D��$��4�R�'l������R�,����by�h2�C2w�@�	)��o�������;(5�w=������p���N�6t���(�����t\���#�&BPh�C�4��>��C�P�FT2<���Q��zL�!��J�������h��z�K��������\��Z\}M��U
�Li1}s�����9N�X� g���i��r��*���
���CX����#�f���5'�f�mM�G��o��t��_���e��f�#�C���{J��
j���Yp
�B��X�����5w���u}k^��>�C5��Mz�D����pp���e��
���`���h$�������H�*�v]��*�����\/�MlDIEDZ�z#����_�QJJ|5
��P�?K��������p1=e�=���|�iG��Y�4}+�L�(�5��d�����g�p#\l�����DF�v��J���G���-5����%���z*s�����=�3z��u�B�y`�q+VY�L���Z�8�T�B��H�d����;2�1C�Z�W��
�T~$����M��z
H;��k
��������)	��TX�,[�R�
O���rcL��1R�$(��z�����@�����Ph�:�*����,�T�S�����/`���f��3�}�
��]BMeH������y�Y���CS��QO=n�����-ph0������[�>�>��}���w������`(8�Y!&��x���6&HJO��)��/L;�U�5�"S����*V~#���MDT�sgF���m�**��dl�t�����,���C�W\��YJ��A<��.2��B��WhV�V���� Na���h&j���"���U��	�L��~����?�U�T
^m���Z�(����4�*����OU���z�)���N�W
����?�`����	�qA��K��.[|��t=)����6�J�rpf2^�P
��+;D
8��x��M�n�=�7{����u#5���+(;,�t�c��2�.*�3�sN����RM�,����,�gj�:��pe���*5%JlBTF|��"�����%5,�����
^�v��#�>�#SW4-&����pyU^�^Ay�1����b��@9=������n@�:O�`=��B�W�kWh�%�'w���O��H����9#�03�p���7���p��{���3%?�
b}@
/�-1�O���#����rSp�z`�a�hk:�����XL{���R��^�rGf�i�AS��O�@�YFi!��t�b��!7�r��G{�Nv�d6=C��������n��Y�&{��j������iv��t5�G�Z��t���a�-����>�d�2[���:���96X���3k�)��D1�����a���?��5��9&ug�����n�6H���!CZ?`bCP��
f	o���T31���m�Ub������u���kM��0
�u,�\��*����=�{p�B���l�3�Y_�5�V_�S��j�v,R]��b�{��IA�<���#*c�I�"��c�"CY\������G��D^*M��!G��A"�z�����E�G���a�NtF
I�M�1t�^�
�[��6���*v���"��P�6�i�[�W���.H��f�&0��@���l�J����U���W���@���^Q�;n��`��=
����(F�v��P�
�����]�1qSGG2�� ��`Q4���Ue``M� U�������]�d�K�����#Pf�q�Rp���!��� G6~N�
8����5(�������&����E4��CJ����k�
-j�_:!i��D��J�	f�>t�6�e$����^��i���{<���J���~�CH@�d�����;��i��;x�/�y��"R��
��B��|��y�������Q��3n-��a�r�������M����h��0L���D'�En<�^H�{7_��r[%�Ed��"I��v#R�	g/�TQTU�<S�I�g)J�����d����m�K"u�A���!���:U1J�N�����9�|�s�����Y6#�p��X�>Q��"�TO\���bR�!�N����u{yZxJ%-:�P��o��N�I�^H��0;�p+*�6�W��b��ux�P*�~e=b���?$�zXs�{0(��k%��mC&��<x�yQ�J����@���N��$"����'����j9�0�[$h�� �-\^'����H�$��i��&�30���zLg���L������l�P4+�h��'�M�A�T���\R���g�>�K8���a\�,�����������u-(
h��V�Y���r�@�a�����Y�j
D�6>��Wth���:R�R����t�2W�}�X{�=(\uf<�0��9�-����)%
�_��}�)�������E�yf�2KD�E&�Q�c�2�Z���F,���L���cD��I�T���m��H�����u���6��!O�i���<�8�u [�����K/�*�,p38M�+�I@���	�`��`�3�g���Hk�D^PW-&��+{T	�h)b��d�i3^Y���B���F��tV���8���N�i�!7����1"�>�l�};����U����/��5���rr1�~�GT�����!E�qD
��5��,�E�`���Gi��|���)�����y����Q0�O�7z~`(�|4�
R�h��W�,�0[����>��vhb�o����i���%�������<��#q��U��u?6	�<��(�����)Q���,W���������	�2�~`�������IG;��������1�j���	�����S��*����M��)D�C
���0\�L�0�R����>�A��6%X?k����'���������Sy�0X����<��k�&����	�e�tn�19J��j�V��b=]\ k����E���)5R-�
m��n�0
��9f���Er5�fPc����Q9�n�� ^5�t��3�
���@.0�B{�V]���/p0\�������RI��.:�Z���0��S�$-�1cjr����[�����[�K��ZJ����5X�4�	9<�U����,.�j;m��2��`���a��h���r������.���>�Z�4�:�h�4�y�y�)���+~;X|u{3yp,�
�y.�2E������r�3=�7��c�|B��H��g�:�V|�#%q�e�W���4��vr2���c����r
���)'
*��5\t�g��B"�y{�\���]T&9�X�����!Q���)�<q>��j\)6�3g����������%P���9��\g�>�q�A�!��m�r���T
�e>��D�<{�������nO��z������>y���^��������y|{�d��7_�����w�����������nN��xrs��Z��={������o�����������������~�����?���O�?^�!�0����|���k�����?zxz��������|������W>z��{x�}y{z��/����<{~[x������|u{���_~�?����|����O?x����[�����f������Z�����Nw����������������~��w?�����/>K?)��������G�|v���7������?y��k/|����G_���>����%���{������o��������}��_���_�����g�������9�������ww���{-��|�?����w����[�����������{��yz���~���������u}��]_��_�������W������z��_~����_��g����k��������
������..�����~y������������;�����>����/����|p��77�������O~t�t�������z�w�u�;��������������y������#�)��?��e��^�W������������s���K���>z����U������E���_w������;������������������>~���^�������������q��Kq������_��gT�w��JAz������I���o�����o(}���wV��w�����U��	��?K��,��})�A����ck�?�����Yw������x}8�e}�?�&��������>���_�����~�����7_�����n��V��O�~R�V�|3���O���
�[�����-?�M���a���}�����O��}���g���x����������������{����7/_�����]^��W_��������h���?���������_|������o���<z���K�I(K�P
#2Simon Riggs
simon@2ndQuadrant.com
In reply to: Alexander Korotkov (#1)
Re: WIP: Access method extendability

On 15 October 2014 13:08, Alexander Korotkov <aekorotkov@gmail.com> wrote:

Postgres was initially designed to support access methods extendability.
This extendability lives to present day. However, this is mostly internal
in-core extendability. One can quite easily add new access method into
PostgreSQL core. But if one try to implement access method as external
module, he will be faced with following difficulties:

...

Problem of WAL is a bit more complex. According to previous discussions, we
don't want to let extensions declare their own xlog records. If we let them
then recovery process will depend on extensions. That is much violates
reliability. Solution is to implement some generic xlog record which is able
to represent difference between blocks in some general manner.

Thank you for progressing with these thoughts.

I'm still a little uncertain about the approach, now my eyes are open
to the problems of extendability.

The main problem we had in the past was that GiST and GIN indexes both
had faulty implementations for redo, which in some cases caused severe
issues. Adding new indexes will also suffer the same problems, so I
see a different starting place.

The faults there raised the need for us to be able to mark specific
indexes as corrupt, so that they could be avoided during Hot Standby
and in normal running after promotion.

Here's the order of features I think we need

1. A mechanism to mark an index as corrupt so that it won't be usable
by queries. That needs to work during recovery, so we can persist a
data structure which tells us which indexes are corrupt. Then
something that checks whether an index is known corrupt during
relcache access. So if we decide an index is bad, we record the index
as corrupt and then fire a relcache invalidation.

2. Some additional code in Autovacuum to rebuild corrupt indexes at
startup, using AV worker processes to perform a REINDEX CONCURRENTLY.

This will give us what we need to allow an AM to behave sensibly, even
in the face of its own bugs. It also gives us UNLOGGED indexes for
free. Unlogged indexes means we can change the way unlogged tables
behave to allow them to truncate down to the highest unchanged data at
recovery, so we don't lose all the data when we crash.

3. That then allows us to move towards having indexes that are marked
"changed" when we perform first DML on the table in any checkpoint
cycle. Which allows us to rebuild indexes which were in the middle of
being changed when we crashed. (The way we'd do that is to have an LSN
on the metapage and then only write WAL for the metapage). The
difference here is that they are UNLOGGED but do not get trashed on
recovery unless they were in the process of changing.

If we do those things, then we won't even need to worry about needing
AMs to write their own WAL records. Recovery will be safe AND we won't
need to go through problems of buggy persistence implementations in
new types of index.

Or put it another way, it will be easier to write new index AMs
because we'll be able to skip the WAL part until we know we want it.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#3Robert Haas
robertmhaas@gmail.com
In reply to: Simon Riggs (#2)
Re: WIP: Access method extendability

On Tue, Oct 28, 2014 at 10:22 AM, Simon Riggs <simon@2ndquadrant.com> wrote:

Or put it another way, it will be easier to write new index AMs
because we'll be able to skip the WAL part until we know we want it.

I like the feature you are proposing, but I don't think that we should
block Alexander from moving forward with a more-extensible WAL format.
I believe that's a general need even if we get the features you're
proposing, which would reduce the need for it. After all, if somebody
builds an out-of-core index AM, ignoring WAL-logging, and then decides
that it works well enough that they want to add WAL-logging, I think
we should make that possible without requiring them to move the whole
thing in-core.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#4Simon Riggs
simon@2ndQuadrant.com
In reply to: Robert Haas (#3)
Re: WIP: Access method extendability

On 28 October 2014 14:53, Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Oct 28, 2014 at 10:22 AM, Simon Riggs <simon@2ndquadrant.com> wrote:

Or put it another way, it will be easier to write new index AMs
because we'll be able to skip the WAL part until we know we want it.

I like the feature you are proposing, but I don't think that we should
block Alexander from moving forward with a more-extensible WAL format.
I believe that's a general need even if we get the features you're
proposing, which would reduce the need for it. After all, if somebody
builds an out-of-core index AM, ignoring WAL-logging, and then decides
that it works well enough that they want to add WAL-logging, I think
we should make that possible without requiring them to move the whole
thing in-core.

I'm not proposing an alternate or additional feature.

I'm saying that the first essential step in adding WAL support to new
AMs is to realise that they *will* have bugs (since with the greatest
respect, the last two AMs from our friends did have multiple bugs) and
so we must have a mechanism that prevents such bugs from screwing
everything else up. Which is the mark-corrupt-index and rebuild
requirement.

We skip straight to the add-buggy-AMs part at our extreme peril. We've
got about 10x as many users now since the 8.x bugs and all the new
users like the reputation Postgres has for resilience. I think we
should put the safety net in place first before we start to climb.

The patch as submitted doesn't have any safety checks for whether the
WAL records refer to persistent objects defined by the AM. At the very
least we need to be able to isolate an AM to only screw up their own
objects. Such checks look like they'd require some careful thought and
refactoring first.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#5Andres Freund
andres@2ndquadrant.com
In reply to: Alexander Korotkov (#1)
Re: WIP: Access method extendability

Hi,

On 2014-10-15 16:08:38 +0400, Alexander Korotkov wrote:

Postgres was initially designed to support access methods extendability.
This extendability lives to present day. However, this is mostly internal
in-core extendability. One can quite easily add new access method into
PostgreSQL core. But if one try to implement access method as external
module, he will be faced with following difficulties:

1. Need to directly insert into pg_am, because of no "CREATE ACCESS
METHOD" command. And no support of dependencies between am and opclasses
etc.
2. Module can't define xlog records. So, new am would be not WAL-logged.

The first problem is purely mechanical. Nothing prevents us to implement
"CREATE ACCESS METHOD" and "DROP ACCESS METHOD" commands and support all
required dependencies.

Problem of WAL is a bit more complex. According to previous discussions, we
don't want to let extensions declare their own xlog records. If we let them
then recovery process will depend on extensions. That is much violates
reliability. Solution is to implement some generic xlog record which is
able to represent difference between blocks in some general manner.

I think this is a somewhat elegant way to attack this problem. But I'm
not so sure it's actually sufficient. Consider e.g. how to deal with hot
standby conflicts? How would you transport the knowledge that there's a
xid conflict to the client?

I guess my question essentially is whether it's actually sufficient for
real world AMs.

The other thing I'm not sure about is that I'm unconvinced that we
really want external AMs...

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#6Oleg Bartunov
obartunov@gmail.com
In reply to: Simon Riggs (#4)
Re: WIP: Access method extendability

On Tue, Oct 28, 2014 at 7:57 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

On 28 October 2014 14:53, Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Oct 28, 2014 at 10:22 AM, Simon Riggs <simon@2ndquadrant.com>

wrote:

Or put it another way, it will be easier to write new index AMs
because we'll be able to skip the WAL part until we know we want it.

I like the feature you are proposing, but I don't think that we should
block Alexander from moving forward with a more-extensible WAL format.
I believe that's a general need even if we get the features you're
proposing, which would reduce the need for it. After all, if somebody
builds an out-of-core index AM, ignoring WAL-logging, and then decides
that it works well enough that they want to add WAL-logging, I think
we should make that possible without requiring them to move the whole
thing in-core.

I'm not proposing an alternate or additional feature.

I'm saying that the first essential step in adding WAL support to new
AMs is to realise that they *will* have bugs (since with the greatest
respect, the last two AMs from our friends did have multiple bugs) and
so we must have a mechanism that prevents such bugs from screwing
everything else up. Which is the mark-corrupt-index and rebuild
requirement.

We skip straight to the add-buggy-AMs part at our extreme peril. We've
got about 10x as many users now since the 8.x bugs and all the new
users like the reputation Postgres has for resilience. I think we
should put the safety net in place first before we start to climb.

agree and we thought about this

The patch as submitted doesn't have any safety checks for whether the
WAL records refer to persistent objects defined by the AM. At the very
least we need to be able to isolate an AM to only screw up their own
objects. Such checks look like they'd require some careful thought and
refactoring first.

the patch Alexander submitted is the PoC, we wanted to hear developers
opinion and
I see no principal objection to work in this direction and we'll continue
to work on all
possible issues.

Show quoted text

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#7Stephen Frost
sfrost@snowman.net
In reply to: Andres Freund (#5)
Re: WIP: Access method extendability

* Andres Freund (andres@2ndquadrant.com) wrote:

The other thing I'm not sure about is that I'm unconvinced that we
really want external AMs...

I was wondering about this also and curious as to if there's been any
prior on-list discussion about this proposal that I've simply missed..?

Would be happy to go back and review earlier discussions, of course, but
I don't recall there being any.

Thanks,

Stephen

#8Simon Riggs
simon@2ndQuadrant.com
In reply to: Stephen Frost (#7)
Re: WIP: Access method extendability

On 28 October 2014 16:19, Stephen Frost <sfrost@snowman.net> wrote:

Would be happy to go back and review earlier discussions, of course, but
I don't recall there being any.

It depends how far back you go.

I think I've had at least 2 tries at writing something, but not in last 5 years.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#9Simon Riggs
simon@2ndQuadrant.com
In reply to: Simon Riggs (#2)
Re: WIP: Access method extendability

On 28 October 2014 14:22, Simon Riggs <simon@2ndquadrant.com> wrote:

Or put it another way, it will be easier to write new index AMs
because we'll be able to skip the WAL part until we know we want it.

To be clear: I am suggesting you do *less* work, not more.

By allowing AMs to avoid writing WAL we get
* higher performance unlogged indexes
* we get fewer bugs in early days of new AMs
* writers of new AMs are OK to avoid majority of hard work and hard testing

So overall, we get new AMs working faster because we can skip writing
the WAL code until we are certain the new AM code is useful and bug
free.

For example, if GIN had avoided implementing WAL it would have been
easier to change on-disk representation.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#10Tom Lane
tgl@sss.pgh.pa.us
In reply to: Stephen Frost (#7)
Re: WIP: Access method extendability

Stephen Frost <sfrost@snowman.net> writes:

* Andres Freund (andres@2ndquadrant.com) wrote:

The other thing I'm not sure about is that I'm unconvinced that we
really want external AMs...

I was wondering about this also and curious as to if there's been any
prior on-list discussion about this proposal that I've simply missed..?

We've touched on the issue a few times, but I don't think there's been
any attempt to define a project policy about it.

My own thought is that allowing external AMs is simply a natural
consequence of PG's general approach to extensibility, and it would
be surprising if we were to decide we didn't want to allow that.

But having said that, it's quite unclear to me that we need the
CREATE/DROP ACCESS METHOD infrastructure proposed here. The traditional
theory about that is that if you're competent to develop an AM at all,
you can certainly manage to insert a row into pg_am manually. I'm afraid
that we'd be adopting and maintaining thousands of lines of code that
won't ever come close to pulling their weight in usefulness, or probably
ever be fully debugged. (The submitted patch is about 1K lines in itself,
and it doesn't appear to address any of the consequences of establishing
an expectation that AMs are something that can be dropped or modified.
Can you say "cache flush"?)

So I'd be inclined to put that part of the patch on the back burner until
there are actually multiple externally maintained AMs that could use it.
Even then, I'm not sure we want to buy into DROP ACCESS METHOD.

I think we *do* need some credible method for extensions to emit WAL
records, though. I've not taken any close look at the code proposed
for that, but the two-sentence design proposal in the original post
sounded plausible as far as it went.

So my vote is to pursue the WAL extensibility part of this, but not the
additional SQL commands.

As for the proposed contrib module, we don't need it to test the WAL
extensibility stuff: we could just rewrite some existing core code to emit
the "extensible" WAL records instead of whatever it's emitting now.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#11Andres Freund
andres@2ndquadrant.com
In reply to: Tom Lane (#10)
Re: WIP: Access method extendability

On 2014-10-28 13:06:52 -0400, Tom Lane wrote:

Stephen Frost <sfrost@snowman.net> writes:

* Andres Freund (andres@2ndquadrant.com) wrote:

The other thing I'm not sure about is that I'm unconvinced that we
really want external AMs...

I was wondering about this also and curious as to if there's been any
prior on-list discussion about this proposal that I've simply missed..?

We've touched on the issue a few times, but I don't think there's been
any attempt to define a project policy about it.

My own thought is that allowing external AMs is simply a natural
consequence of PG's general approach to extensibility, and it would
be surprising if we were to decide we didn't want to allow that.

It'd be entirely politicial. I agree. I'm pretty unhappy with the
thought that we end up with several 'for pay' index ams out there. But
then, PG is BSD style licensed.

What I think we need to make absolutely sure is that we preserve the
freedom to tinker with the AM functions. I think we'll very heavily
curse ourselves if we can't as easily add new features there anymore.

But having said that, it's quite unclear to me that we need the
CREATE/DROP ACCESS METHOD infrastructure proposed here. The traditional
theory about that is that if you're competent to develop an AM at all,
you can certainly manage to insert a row into pg_am manually.

The problem with doing that is that you not only need to add a row in
pg_am, but also pg_depend. And a way to remove that row when the
respective extension is dropped. Especially the latter imo changed the
landscape a fair bit.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#12Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#11)
Re: WIP: Access method extendability

Andres Freund <andres@2ndquadrant.com> writes:

On 2014-10-28 13:06:52 -0400, Tom Lane wrote:

But having said that, it's quite unclear to me that we need the
CREATE/DROP ACCESS METHOD infrastructure proposed here. The traditional
theory about that is that if you're competent to develop an AM at all,
you can certainly manage to insert a row into pg_am manually.

The problem with doing that is that you not only need to add a row in
pg_am, but also pg_depend.

(1) That's not that hard; initdb makes pg_depend entries from SQL.
(2) You only need a row in pg_depend if you provide a DROP command
that would need to pay attention to it.

And a way to remove that row when the
respective extension is dropped.

I'm not at all sold on the idea that we need to support dropping AMs.
I think it'd be fine to consider that installing an AM into a given
database is a one-way operation. Then you just need to insert some
pg_depend entries that "pin" the AM's individual functions, and you're
done.

Yeah, sure, CREATE/DROP ACCESS METHOD would be cleaner. But in this
case I'm not buying the "if you build it they will come" argument.
External AMs *can* be built without any such SQL-level support, and if
there were really much demand for them, there would be some out there.
Let's build the essential WAL support first, and leave the syntactic
sugar till we see some demand.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#13Simon Riggs
simon@2ndQuadrant.com
In reply to: Tom Lane (#10)
Re: WIP: Access method extendability

On 28 October 2014 17:06, Tom Lane <tgl@sss.pgh.pa.us> wrote:

My own thought is that allowing external AMs is simply a natural
consequence of PG's general approach to extensibility, and it would
be surprising if we were to decide we didn't want to allow that.

If it wasn't clear from my two earlier attempts, yes, +1 to that.

I'd like to avoid all of the pain by making persistent AMs that are
recoverable after a crash, rather than during crash recovery.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#14Andres Freund
andres@2ndquadrant.com
In reply to: Simon Riggs (#13)
Re: WIP: Access method extendability

On 2014-10-28 17:45:36 +0000, Simon Riggs wrote:

I'd like to avoid all of the pain by making persistent AMs that are
recoverable after a crash, rather than during crash recovery.

Besides the actual difficulities of supporting this, imo not being
available on HS and directly after a failover essentially makes them
next to useless.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#15Jim Nasby
Jim.Nasby@BlueTreble.com
In reply to: Simon Riggs (#2)
Re: WIP: Access method extendability

On 10/28/14, 9:22 AM, Simon Riggs wrote:

2. Some additional code in Autovacuum to rebuild corrupt indexes at
startup, using AV worker processes to perform a REINDEX CONCURRENTLY.

I don't think loading more functionality into autovac is the right way to do that.
--
Jim Nasby, Data Architect, Blue Treble Consulting
Data in Trouble? Get it in Treble! http://BlueTreble.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#16Tom Lane
tgl@sss.pgh.pa.us
In reply to: Simon Riggs (#13)
Re: WIP: Access method extendability

Simon Riggs <simon@2ndQuadrant.com> writes:

On 28 October 2014 17:06, Tom Lane <tgl@sss.pgh.pa.us> wrote:

My own thought is that allowing external AMs is simply a natural
consequence of PG's general approach to extensibility, and it would
be surprising if we were to decide we didn't want to allow that.

If it wasn't clear from my two earlier attempts, yes, +1 to that.

I'd like to avoid all of the pain by making persistent AMs that are
recoverable after a crash, rather than during crash recovery.

I think the notion of having AMs that explicitly don't have WAL support
is quite orthogonal to what's being discussed in this thread. It might
be worth doing that just to get the hash AM into a less-weird state
(given that nobody is stepping up to the plate to fix it properly).

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#17Andres Freund
andres@2ndquadrant.com
In reply to: Tom Lane (#12)
Re: WIP: Access method extendability

On 2014-10-28 13:37:33 -0400, Tom Lane wrote:

I'm not at all sold on the idea that we need to support dropping AMs.
I think it'd be fine to consider that installing an AM into a given
database is a one-way operation. Then you just need to insert some
pg_depend entries that "pin" the AM's individual functions, and you're
done.

I think that'd be somewhat ugly. An extension adding such a AM would
then either actively need to block dropping (e.g. by pinned entries, as
you mention) or do rather odd things on recreation. I think that'd be
dropping our own standards.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#18Alexander Korotkov
aekorotkov@gmail.com
In reply to: Simon Riggs (#9)
Re: WIP: Access method extendability

On Tue, Oct 28, 2014 at 8:04 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

On 28 October 2014 14:22, Simon Riggs <simon@2ndquadrant.com> wrote:

Or put it another way, it will be easier to write new index AMs
because we'll be able to skip the WAL part until we know we want it.

To be clear: I am suggesting you do *less* work, not more.

By allowing AMs to avoid writing WAL we get
* higher performance unlogged indexes
* we get fewer bugs in early days of new AMs
* writers of new AMs are OK to avoid majority of hard work and hard testing

So overall, we get new AMs working faster because we can skip writing
the WAL code until we are certain the new AM code is useful and bug
free.

For example, if GIN had avoided implementing WAL it would have been
easier to change on-disk representation.

Major problem of changing on-disk representation is that we have to support
both binary formats because of pg_upgrade. This problem is even burdened
with WAL, because WAL record redo function also have to support both
formats. However, it's also quite independent of WAL.

Having access methods as extensions can significantly improves situations
here. Imagine, GIN was an extension. One day we decide to change its binary
format. Then we can issue new extension, GIN2 for instance. User can
upgrade from GIN to GIN2 in following steps:

1. CREATE EXTENSION gin2;
2. CREATE INDEX CONCURRENTLY [new_index] USING gin2 ([index_def]);
3. DROP INDEX CONCURRENTLY [old_index];
4. DROP EXTENSION gin;

No need to write and debug the code which reads both old and new binary
format. For sure, we need to support old GIN extension for some time. But,
we have to support old in-core versions too.

Unfortunately, I didn't mention this in the first post because I consider
this as a serious argument for extensible AMs.

Also, I'm not sure that many users have enough of courage to use unlogged
AMs. Absence of durability is only half of trouble, another half is lack of
streaming replication. I think if we have unlogged GIN then external
indexing engines would be used by majority of users instead of GIN.

------
With best regards,
Alexander Korotkov.

#19ktm@rice.edu
ktm@rice.edu
In reply to: Tom Lane (#16)
Re: WIP: Access method extendability

On Tue, Oct 28, 2014 at 01:51:21PM -0400, Tom Lane wrote:

Simon Riggs <simon@2ndQuadrant.com> writes:

On 28 October 2014 17:06, Tom Lane <tgl@sss.pgh.pa.us> wrote:

My own thought is that allowing external AMs is simply a natural
consequence of PG's general approach to extensibility, and it would
be surprising if we were to decide we didn't want to allow that.

If it wasn't clear from my two earlier attempts, yes, +1 to that.

I'd like to avoid all of the pain by making persistent AMs that are
recoverable after a crash, rather than during crash recovery.

I think the notion of having AMs that explicitly don't have WAL support
is quite orthogonal to what's being discussed in this thread. It might
be worth doing that just to get the hash AM into a less-weird state
(given that nobody is stepping up to the plate to fix it properly).

regards, tom lane

Hi,

I think that someone is working on the hash index WAL problem, but are
coming up to speed on the whole system, which takes time. I know that
I have not had a large enough block of time to spend on it either. :(

Regards,
Ken

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#20Stephen Frost
sfrost@snowman.net
In reply to: Alexander Korotkov (#18)
Re: WIP: Access method extendability

* Alexander Korotkov (aekorotkov@gmail.com) wrote:

Having access methods as extensions can significantly improves situations
here. Imagine, GIN was an extension. One day we decide to change its binary
format. Then we can issue new extension, GIN2 for instance. User can
upgrade from GIN to GIN2 in following steps:

We could support this without having GIN be an extension by simply
having a GIN2 in core also, so I don't buy off on this being a good
reason for extensions to provide AMs. For my 2c, I'm pretty happy with
the general idea of "read-old, write-new" to deal with transistions.

It's more complicated, certainly, but I don't think trying to force
users off of an old version is actually going to work all that well and
we'd just end up having to support both the old and new extensions
indefinitely anyway.

Thanks,

Stephen

#21Simon Riggs
simon@2ndQuadrant.com
In reply to: Andres Freund (#14)
Re: WIP: Access method extendability

On 28 October 2014 17:47, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-10-28 17:45:36 +0000, Simon Riggs wrote:

I'd like to avoid all of the pain by making persistent AMs that are
recoverable after a crash, rather than during crash recovery.

Besides the actual difficulities of supporting this, imo not being
available on HS and directly after a failover essentially makes them
next to useless.

Broken WAL implementations are worse than useless.

I'm saying we should work on how to fix broken indexes first, before
we allow a crop of new code that might cause them.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#22Simon Riggs
simon@2ndQuadrant.com
In reply to: Alexander Korotkov (#18)
Re: WIP: Access method extendability

On 28 October 2014 17:58, Alexander Korotkov <aekorotkov@gmail.com> wrote:

Also, I'm not sure that many users have enough of courage to use unlogged
AMs. Absence of durability is only half of trouble, another half is lack of
streaming replication. I think if we have unlogged GIN then external
indexing engines would be used by majority of users instead of GIN.

Please answer the problem I have raised.

How will you act when the new AM that you write breaks?
How will you advise your users that the durability they sensibly
desire is actually causing them data loss?

I haven't opposed your ideas in this patch; I have only observed the
necessary surrounding infrastructure is incomplete.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#23Simon Riggs
simon@2ndQuadrant.com
In reply to: Jim Nasby (#15)
Re: WIP: Access method extendability

On 28 October 2014 17:50, Jim Nasby <Jim.Nasby@bluetreble.com> wrote:

On 10/28/14, 9:22 AM, Simon Riggs wrote:

2. Some additional code in Autovacuum to rebuild corrupt indexes at
startup, using AV worker processes to perform a REINDEX CONCURRENTLY.

I don't think loading more functionality into autovac is the right way to do
that.

You'd need to explain why and/or suggest your right way.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#24Andres Freund
andres@2ndquadrant.com
In reply to: Simon Riggs (#21)
Re: WIP: Access method extendability

On 2014-10-28 20:17:57 +0000, Simon Riggs wrote:

On 28 October 2014 17:47, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-10-28 17:45:36 +0000, Simon Riggs wrote:

I'd like to avoid all of the pain by making persistent AMs that are
recoverable after a crash, rather than during crash recovery.

Besides the actual difficulities of supporting this, imo not being
available on HS and directly after a failover essentially makes them
next to useless.

Broken WAL implementations are worse than useless.

I'm saying we should work on how to fix broken indexes first, before
we allow a crop of new code that might cause them.

Why do we presume all of them will be that buggy? And why is that
different for nbtree, gin, gist? And how is any form of automated
invalidation changing anything fundamentally?

To me this is a pretty independent issue.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#25Simon Riggs
simon@2ndQuadrant.com
In reply to: Andres Freund (#24)
Re: WIP: Access method extendability

On 28 October 2014 23:25, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-10-28 20:17:57 +0000, Simon Riggs wrote:

On 28 October 2014 17:47, Andres Freund <andres@2ndquadrant.com> wrote:

On 2014-10-28 17:45:36 +0000, Simon Riggs wrote:

I'd like to avoid all of the pain by making persistent AMs that are
recoverable after a crash, rather than during crash recovery.

Besides the actual difficulities of supporting this, imo not being
available on HS and directly after a failover essentially makes them
next to useless.

Broken WAL implementations are worse than useless.

...because the indexes are also unavailable during HS.

I'm saying we should work on how to fix broken indexes first, before
we allow a crop of new code that might cause them.

Why do we presume all of them will be that buggy? And why is that
different for nbtree, gin, gist? And how is any form of automated
invalidation changing anything fundamentally?

The current system does not allow for the possibility of a corruption
bug. If one occurs, the only thing an AM can do is PANIC. It has no
mechanism to isolate the problem and deal with it, which affects the
whole server.

So the issue is one of risk of PANIC or data loss - things we have
always taken strong measures against. That is all I have requested as
a first step. And I request it because I remember and dealt with many
bugs and user problems in earlier times of 6-9 years ago.

You are also right: btree, GIN and GIST will benefit from this also.

To me this is a pretty independent issue.

Initial users of GiST and GIN were rare. The clear target here is
indexing of JSONB data. I don't expect the users of that to be rare. I
expect adoption to be rapid and the effect of bugs to be widespread.

So I see the ability to report bugs and prevent data loss as essential
in the context of new AMs. Automatic fixing of the problem could be
fairly easy, but might be regarded as nice to have, as long as manual
fixing of the problem is easily possible.

I don't regard any of this as an insult or comment that certain people
write buggy code, while others are better people. Everybody has bugs
and WAL code in complex new index types is complex enough that it is
more likely than other places. I salute those who write innovative new
code for Postgres.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#26Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#24)
Re: WIP: Access method extendability

On Tue, Oct 28, 2014 at 7:25 PM, Andres Freund <andres@2ndquadrant.com> wrote:

To me this is a pretty independent issue.

I quite agree. As Stephen was at pains to remind me last night on
another thread, we cannot force people to write the patches we think
they should write. They get to pursue what they think the priorities
are, not what anyone else thinks they are. Of course we can and
should block patches that we think are a bad idea, or that are
badly-designed or badly-implemented for what they are, but we cannot
and should not block someone who feels that the first priority is A
just because we think it is B or C.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#27Simon Riggs
simon@2ndQuadrant.com
In reply to: Simon Riggs (#25)
Re: WIP: Access method extendability

On 29 October 2014 09:27, Simon Riggs <simon@2ndquadrant.com> wrote:

The current system does not allow for the possibility of a corruption
bug. If one occurs, the only thing an AM can do is PANIC. It has no
mechanism to isolate the problem and deal with it, which affects the
whole server.

So the issue is one of risk of PANIC or data loss - things we have
always taken strong measures against. That is all I have requested as
a first step. And I request it because I remember and dealt with many
bugs and user problems in earlier times of 6-9 years ago.

You are also right: btree, GIN and GIST will benefit from this also.

Since I feel this is a real concern, I will contribute this feature,
outside of the current patch.

--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#28Jim Nasby
Jim.Nasby@BlueTreble.com
In reply to: Simon Riggs (#23)
Re: WIP: Access method extendability

On 10/28/14, 3:27 PM, Simon Riggs wrote:

On 28 October 2014 17:50, Jim Nasby <Jim.Nasby@bluetreble.com> wrote:

On 10/28/14, 9:22 AM, Simon Riggs wrote:

2. Some additional code in Autovacuum to rebuild corrupt indexes at
startup, using AV worker processes to perform a REINDEX CONCURRENTLY.

I don't think loading more functionality into autovac is the right way to do
that.

You'd need to explain why and/or suggest your right way.

Why wouldn't we register it as a background worker?

Not only doesn't this have anything to do with vacuum, but it should operate differently as well: once we've rebuilt everything that needs to be rebuilt the process should go away until the next startup. That's the opposite of what autovac does.

The one potential commonality I see is having a launcher process that's responsible for launching multiple workers (if we want to be rebuilding multiple indexes at once), but AFAICT that capability is also provided by bgworker.c.
--
Jim Nasby, Data Architect, Blue Treble Consulting
Data in Trouble? Get it in Treble! http://BlueTreble.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#29Andres Freund
andres@2ndquadrant.com
In reply to: Jim Nasby (#28)
Re: WIP: Access method extendability

On 2014-10-29 14:33:00 -0500, Jim Nasby wrote:

On 10/28/14, 3:27 PM, Simon Riggs wrote:

On 28 October 2014 17:50, Jim Nasby <Jim.Nasby@bluetreble.com> wrote:

On 10/28/14, 9:22 AM, Simon Riggs wrote:

2. Some additional code in Autovacuum to rebuild corrupt indexes at
startup, using AV worker processes to perform a REINDEX CONCURRENTLY.

I don't think loading more functionality into autovac is the right way to do
that.

You'd need to explain why and/or suggest your right way.

Why wouldn't we register it as a background worker?

Not only doesn't this have anything to do with vacuum, but it should operate differently as well: once we've rebuilt everything that needs to be rebuilt the process should go away until the next startup. That's the opposite of what autovac does.

That's pretty much how autovac workers work. Do stuff until not needed
anymore. The difference is that you have a process that starts them.

It'd not be a good idea to throw this together with user defined
bgworkers because there's a finite number of slots for them. So at the
very least we'd need a separate pool for system bgworkers. Which would
persistently take up resources (PGPROC entries et al). So it seems
better to use the existing pool of autovac workers.

The one potential commonality I see is having a launcher process that's responsible for launching multiple workers (if we want to be rebuilding multiple indexes at once), but AFAICT that capability is also provided by bgworker.c.

There really is no need to use bgworkers for builtin things. They are
useful because they allow extensions to do what in core already could do
for a long time.

Greetings,

Andres Freund

PS: You mails would be easier to read if htey had sane line lenghts...

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#30Alexander Korotkov
aekorotkov@gmail.com
In reply to: Andres Freund (#29)
Re: WIP: Access method extendability

Hi!

Thanks everybody for discussion. Sorry for delay. I'll try to address most
of questions arised in this discussion.

In general, I'm agree with concept of marking index as invalid in certain
cases.
I see following possible consequences of buggy WAL-logged custom AM:
1) Server crash during insert/update.
2) Server crash during select.
3) Invalid query answers.
4) Server crash during vacuum.
5) Server crash in recovery.

#5 is totally unacceptable. I've tried to design generic WAL record so that
it should be always possible to purely mechanically apply the record. It's
always possible to move piece of memory inside the page. It's always
possible to copy piece of memory from WAL-record to the page. Buggy WAL for
sure could cause an index corruption as well as any other bug in AM. WAL
support doesn't bring nothing special to this expect the fact that WAL is
quite hard to debug.

However, in current implementation I missed some hidden asserts about page
structure. Page could be so corrupted that we can't, for instance, safely
call XLogReadBufferForRedo(). All this cases must be worked out. If we
can't update page during recovery, index must be marked as invalid. It's
some amount of work, but it seems to be very feasible.

#4 seems problematic too. If autovacuum crashes on some index, then
postgres can enter a crash loop. So, if autovacuum crashes on extension
provided AM, that index should be marked as invalid.

Consequences #1, #3 and #3 could be easily caused by buggy opclass as well.
The reason why we're not knee-deep in extension provied bugs in GiST/GIN
opclasses is not easyness of writing correct GiST/GIN opclasses. Actual
reason is that we have only few GiST/GIN opclasses which weren't written by
GiST/GIN authors.

So, I don't expect PostgreSQL to be flooded with buggy AMs once we add AM
extendability. Our motivation behind this proposal is that we want to be
able to develop custom extensions with AMs. We want to be able to provide
our AMs to our customers whithout having to push that into PostgreSQL core
or fork PostgreSQL. Bugs in that AMs in our responsibility to out
customers. Some commercial companies could implement patented AMs (for
instance, fractal tree), and that is their responsibility to their
customers.

Also, I think it's OK to put adopting custom AMs to changes of AM interface
to authors of those custom AMs. AM extensions anyway should be adopted to
each new major release. AFAIR, interface of RelationOpen() function has
been changed not too long ago. Custom AM would use many functions which we
use to access relations. Their interface can be changed in the next release.

PostGIS GiST opclass has bugs which are reproducable, known and not fixed.
This is responsibility of PostGIS to their customers. We can feel sorry for
PostGIS that they are so slow on fixing this. But we shouldn't feel sorry
for GiST extendability, that woulde be redicilous.

Some recearches could write their own extensions. We can hope that they are
smart enough to not recommend it for production use. We can back our hope
with warning during installing extension provided AM. That warning could
say that all corruption caused by extension provided AM is up to AM
developer. This warning could make users to beware of using extension
provided AMs in production
unless they are fully trust extension developer (have support subscription
if it's commercial).

PostgreSQL doesn't have any kind of safe extensions. Every extension must
be trusted. Every extension can break (almost) everything.When one types
CREATE EXTENSION he must completely trust extension author. This applies to
every extension.

I would be very careful with discouraging commercial AM extensions. We
should always keen in the mind how many of PostgreSQL developers are
working for companies which own their commercial PostgreSQL forks and how
big their contribution is. Patented extensions looks scary for sure. But
it's up to software patents not up to PostgreSQL extendability...

Particular steps I'm going to do on these patches:
1) Make generic_redo never crash on broken pages.
2) Make autovacuum launcher mark index as invalid if vacuum process crashed
on custom AM index. Since, we can't write something into postgres cluster
when one process has crushed, ITSM autovacuum should have some separate
place to put this information. Thus, after restart postgres can read it and
mark index as invalid.

Don't allowing CREATE ACCESS METHOD command seems problematic for me. How
could it work with pg_upgrade? pg_dump wouldn't dump extra pg_am records.
So, pg_upgrade would break at creating operator classes on new cluster. So,
I agree with dropping create am command only if we let pg_dump to dump
extra pg_am records...

------
With best regards,
Alexander Korotkov.

#31Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Alexander Korotkov (#30)
Re: WIP: Access method extendability

On 11/10/2014 10:30 PM, Alexander Korotkov wrote:

Don't allowing CREATE ACCESS METHOD command seems problematic for me. How
could it work with pg_upgrade? pg_dump wouldn't dump extra pg_am records.
So, pg_upgrade would break at creating operator classes on new cluster. So,
I agree with dropping create am command only if we let pg_dump to dump
extra pg_am records...

pg_dump would dump the CREATE EXTENSION command, and the extension's
installation script inserts the row to pg_am. pg_dump doesn't dump
objects that are part of an extension, so that's how this would work
with the CREATE ACCESS METHOD command, too.

Backtracking a bit, to summarize the discussion so far:

* It would be nice to have indexes that are not WAL-logged, but are
automatically re-created after a crash. But it's a completely different
and orthogonal feature, so there's no need to discuss that further in
this thread.

* If an extension is buggy, it can crash and corrupt the whole database.
There isn't really anything we can do about that, and this patch doesn't
make that any better or worse.

* CREATE ACCESS METHOD command isn't worth it.

Looking at the patch itself:

* It has bitrotted, thanks to my WAL format changes.

* The memcpy/memmove based API seems difficult to use correctly. Looking
at the bloom filter example, it seems that you need a lot more code to
implement WAL-logging with this, than you would with a rmgr-specific
redo function. I was hoping this to make it simpler.

I think the API has to be more high-level. Or at least also provide a
higher-level API, in addition to the low level one. Looking at the
indexams we have, almost all pages use the standard page layout, with
PageAddItem etc., plus a metapage, plus some indexam-specific metadata
in the special area. The proposed API makes it pretty complicated to
deal with that common case. After calling PageAddItem, you need intimate
knowledge of what PageAddItem did, to create a correct WAL record for
it. For PageIndexMultiDelete, it's next to impossible.

I think we'll have to accept that the WAL records with this generic API
are going to be much bulkier than ones tailored for the AM. We could
provide a compact representation for common operations like PageAddItem,
but for any more complicated operations, just WAL-log a full page image,
as it's too fiddly to track the changes at a finer level. For any
serious performance critical stuff, you'll just have to write an
old-fashioned rmgr.

(marking this as "returned with feedback" in the commitfest)

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#32Alexander Korotkov
aekorotkov@gmail.com
In reply to: Heikki Linnakangas (#31)
Re: WIP: Access method extendability

Hi, Heikki!

Thank you for summarizing. In general, I agree with your notes with
some exceptions.

On Mon, Nov 24, 2014 at 1:52 PM, Heikki Linnakangas <hlinnakangas@vmware.com

wrote:

On 11/10/2014 10:30 PM, Alexander Korotkov wrote:

Don't allowing CREATE ACCESS METHOD command seems problematic for me. How
could it work with pg_upgrade? pg_dump wouldn't dump extra pg_am records.
So, pg_upgrade would break at creating operator classes on new cluster.
So,
I agree with dropping create am command only if we let pg_dump to dump
extra pg_am records...

pg_dump would dump the CREATE EXTENSION command, and the extension's
installation script inserts the row to pg_am. pg_dump doesn't dump objects
that are part of an extension, so that's how this would work with the
CREATE ACCESS METHOD command, too.

In binary upgrade mode pg_dump have to guarantee that all database objects
will have same oids. That's why in binary upgrade mode pg_dump dumps
extension contents instead of just CREATE EXTENSION command.

Backtracking a bit, to summarize the discussion so far:

* It would be nice to have indexes that are not WAL-logged, but are
automatically re-created after a crash. But it's a completely different and
orthogonal feature, so there's no need to discuss that further in this
thread.

* If an extension is buggy, it can crash and corrupt the whole database.
There isn't really anything we can do about that, and this patch doesn't
make that any better or worse.

* CREATE ACCESS METHOD command isn't worth it.

Taking into account my previous note, how can custom extensions survive
pg_upgrade without CREATE ACCESS METHOD command?

------
With best regards,
Alexander Korotkov.

#33Alexander Korotkov
aekorotkov@gmail.com
In reply to: Alexander Korotkov (#32)
3 attachment(s)
Re: WIP: Access method extendability

Hackers,

there is next revision of patches providing access method extendability.
Now it's based on another patch which reworks access method interface.
/messages/by-id/CAPpHfdsXwZmojm6Dx+TJnpYk27kT4o7Ri6X_4OSWcByu1Rm+VA@mail.gmail.com

Besides access method interface, major change is generic xlog interface.
Now, generic xlog interface is more user friendly. Generic xlog compares
initial and changed versions of page by itself. The only thing it can't do
is to find data moves inside page, because it would be too high overhead.
So in order to get compact WAL records one should use
GenericXLogMemmove(dst, src, size) in order to move data inside page. If
this function wasn't used then WAL records would just not so compact.

In general pattern of generic WAL usage is following.

1) Start using generic WAL: specify relation

GenericXLogStart(index);

2) Register buffers

GenericXLogRegister(0, buffer1, false);
GenericXLogRegister(1, buffer2, true);

first argument is a slot number, second is the buffer, third is flag
indicating new buffer

3) Do changes in the pages. Use GenericXLogMemmove() if needed.

4) Finish using GenericXLogFinish(), or abort using GenericXLogAbort(). In
the case of abort initial state of pages will be reverted.

Generic xlog takes care about critical section, unlogged relation, setting
lsn, making buffer dirty. User code is just simple and clear.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

Attachments:

create-am.2.patch.gzapplication/x-gzip; name=create-am.2.patch.gzDownload
generic-xlog.2.patch.gzapplication/x-gzip; name=generic-xlog.2.patch.gzDownload
bloom-contrib.2.patch.gzapplication/x-gzip; name=bloom-contrib.2.patch.gzDownload
#34Teodor Sigaev
teodor@sigaev.ru
In reply to: Alexander Korotkov (#33)
Re: WIP: Access method extendability

In general pattern of generic WAL usage is following.

1) Start using generic WAL: specify relation

M-m, what about extensions which wants to use WAL but WAL record doesn't
connected to any relation? For example, transaction manager or kind of FDW.

GenericXLogStart(index);

2) Register buffers

GenericXLogRegister(0, buffer1, false);
GenericXLogRegister(1, buffer2, true);

first argument is a slot number, second is the buffer, third is flag indicating
new buffer

Why do we need a slot number? to replace already registered buffer?

--
Teodor Sigaev E-mail: teodor@sigaev.ru
WWW: http://www.sigaev.ru/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#35Alexander Korotkov
aekorotkov@gmail.com
In reply to: Teodor Sigaev (#34)
3 attachment(s)
Re: WIP: Access method extendability

On Tue, Sep 1, 2015 at 6:50 PM, Teodor Sigaev <teodor@sigaev.ru> wrote:

In general pattern of generic WAL usage is following.

1) Start using generic WAL: specify relation

M-m, what about extensions which wants to use WAL but WAL record doesn't
connected to any relation? For example, transaction manager or kind of FDW.

GenericXLogStart(index);

2) Register buffers

GenericXLogRegister(0, buffer1, false);
GenericXLogRegister(1, buffer2, true);

first argument is a slot number, second is the buffer, third is flag
indicating
new buffer

Why do we need a slot number? to replace already registered buffer?

For further simplification slot number could be omitted. In the attached
patches, generic xlog replay applies changes in the same order
GenericXLogRegister was called.
Patches was rebased against latest version of am interface rework patch.
/messages/by-id/CAPpHfduGY=KZSBPZN5+USTXev-9M2PAUp3Yi=SYFDo2N244P-A@mail.gmail.com

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

Attachments:

create-am.3.patch.gzapplication/x-gzip; name=create-am.3.patch.gzDownload
generic-xlog.3.patch.gzapplication/x-gzip; name=generic-xlog.3.patch.gzDownload
bloom-contrib.3.patch.gzapplication/x-gzip; name=bloom-contrib.3.patch.gzDownload
���U��k{�����%��P �Y�M��HP�Er���GOh�=�0�!�s������weU7����h?�U����bE����(�w��t��??��.��o>�n���hz<��\������,^;�N27�eygzz8�%{s�W�����k���{wM�w�������������z���^�0���R���,\�
��J��{��d�����dzz6>�q������������}��l����xz�?����x6;������d��6^�=_�������y��p������������T�w���z\�G�[��&��o���(��F�������}��SvE�e����x�����7��~�
��_������bz<�?��MO���E�u~0>���4>��8������������E�jwo��������{O_<��x�����w����������'O��>��{��~/g����|��L�f���l>}{:^\�Olt�����,�����W�_���
��?=:�e�������O{�Q��^<��k���}?@G����?�e_<��179>�?��[~<���2/6==8���.�)���xt~��pz�]ra1;�s1=>����~��RC��>���y���x�f||�nO�W���=������[�^����W�;����$g��
R���7�[/�4�N��t�wI�������~�N����}���f��/������<���O~�6����?��_lmg/�'o���������c�����xz�d~q�o���;��_��1������������G~���'������	K�������z��_.����c/��b��w������}6^���X�w��g���O��~�i��$��x���.���������a��N�������i�}|�}t[����|���x7>���P����?3�.>=��'���]�f��x���b�e��lv�E�����|>
}�M�����waf���t�gbvz�>�5x������iqn�q����x��ou6>��g�����LB������vv:����ngA�������nvq|����W���?~oo:����.�������d|~�.;�����k���3��b|���q8��O?]d��3��B��������}zrv<9�����fG���g��w����>9��������p:�?;�y���x�����wm���Y6��O��6Og���<N�?M��� �6H��L�`�Yz���|2v,��M��@���[0��~b��x�nr����������d|����c���#����&����ON�.�e����bz�p�����J�k����vvzq������t�0��}
����'���p;�V��l3s���|?�?�Q�f[(���_�~�����>z�U��������2�����=j�����i�=-��~�����o�M{���vx�{���9?����{�7�s/�?O�/~��%��<��pi`�Q|�8�����d��_�g^h�B���������2^�������x}x��������[8���~@A:m�8�������5����/-�}P����������'&]~ZL�����.��������a�T7�NN�7'���M6���nF[w��#��lm}��4,�y�_����~���_,��_����~���_l��_�E7�������~���"�n~y�z	*��C%
����b:V�t�G�^��d�9������`��V�Q���
6��w��B��O�s��������G'h���#���R����)�?��c|��?'j�nvt�_����$lc^��L���CW=�a���/����������������B���.������^�����6������F��W��<;�{�C�U�a�k�-o^,�e�-�'�������?��[G~�����l�����:{4;=�"�����C�)ne;����������{_��=����%����������=]�`�bKgwv+����t��l�?�8
����[���-f��O�����e�eo�x�~9o4x_(X���
�o{����������
�@���k���+��f=�����y�w�c�`>����K�]g{���<�6���?����~��gg+���-�����|���n���M$�*iY@��<�.�p������f-������Ho�>�J���w�I�������66���9�����������:.�h��G�������^
��D9�����=�������Be�'?M�gg�-��>�]�
Zx�x����O�>�y���s�J�]1d�k��p|�Q�j�Y����&���N�����g�3?b��9�<vr�j�N?�: ���&����M����\l�<�zC���������b�v��c���=���n��" 1a!���������,N��8{{�n��O�����?M���zk�Sb�r�Iz�]y4;������=|q�����1������� ���gz�����<~�u0���W��0s�����f�g������Em;���0c�B���nX�������_,���6C�d�n|79��,6?���F�Y0cgGQ����}�c�~
0Y��t>]��
���������Fl����I��v�C���/�����O��6�����xIFL,LB���og�B����!�������')Y�9}<�u(=�-��h����p�_�W��'�)u]�A�������P���{k{�|s\�����=u���q�P�pSR��-�}�������L���5>Y�������9��]��`������q�\<�zW"�-W/�������o.�m.��<�_��m/'�������������	~����Gh����I����b����b�����4����w��_���G�hco���=������z�����!~>=]���5U\���sO�l!�����/���=_]M�7_Is�Q�^m$��=v4�N�U���������i2���%;�����F�8�<��jB�����v��mbw�:8{�i�=Y����m[�Y��{_�Q������LO��w����������O��	��IS�k��M�G~�z3>����p"nN�;�lg�������O������O�lv����������W����w��?M����+��<1���S��r���U�5��G��wbw7�V���9�{|�K_��y�8x�z�9r�4��I������������U������_�)�q�[<�����>]LN�h��(��lefG�V�c�s��:`��l<�f����O^�������W��|���_��9�F�����E�����T|�~�^����(=����
��������vP��<<��n���~�U(+\~�%�{�aT���A����}�@�^��$Z&y[����du��G��6���*�����8~�_�!�������]s��u�6�wF�����9V��.z[������^����Lc�k(����y�cH���WAm����G^0l������w�����!|��8I�]4x�~}>>����^��3�{�9C����`~+��������f�BZH�������\�����o�W>�!��=\f��*[��w�����9�����(�f��.�u~�X���E��c�<�����[4�6����Wo�y���������w��|����wO��=����{����[������}�u/)��ku�U�����|~I����?��c�o��i�����	��K]����M�o��O����/u�K�����u--��c/z���X�IM�����Z��K/��&����ri���ap�����������n��dqq~��z-����w/���+v��������i�Sr���[�:f6X�qf���d�.[���<�"/g���
�}�v� �����O���<�Q�k���]��%Wih�Fl�u���+W�����0�A7�W�6���_���{��&��F�^���OgQ[��.��_�%��QX�m��
���-"Hq��q���������
�Ez����������nMy�����_y�����z���w�����y�����W��}����O��u�����'����P|������]y�&Z�j�:�������E����{_�����h�A���#!�2[<�����K��~H~��<�k��ub��r����y���Lo�����-�Y����=�~���kU��P�9C7yX��+p��Z�neC���q�`;����_;[����c����J����������*Om���;���q����
[�"(��m���!�������Q�a�V�&sc���� ��WO_���>
��=���pYb���zYlX@/��W�[���'��z�R��L��O|�����H��.��y{�0��V�������{���+�vuY�zY���N�����l?+��x��+�i4����w��/�yx�+�a�V?.��_����p�~�m>����A���d������^6�p��,�pm���7%!���
/�Al������������".��^��_��^��|��h7{��s�`������|��v_��������}.�K;{���^������w������<��{o2f��G�s����<z�������G��y������}+/<��;��}������g;{���rv�1���'���}�/f^�>�����E��&6����p�v�3��lh`��w���p!�#�Y�� oWv#\�����w��F��ID��D�~�lv����ui�^`�������6�s���;n���G�a��;9
y-wB��I�#?�>���� �d���49���S����������8��`����x���������Sc��z��������xw����j�v���k�������/����i������~��0=�,}vgM�{|2>���P8��=��K��u��*��-�h�j�_`����}�����G��p��>�J�t����|��l��_��_����s���^jb90�;�/����� �%��d�L�
�~����m�+������[sGvw��Ko9��766�������w����}���syx�����7�;��mn�<w+�����5>D����h���/6Bj������omCO�z���8��'�1��9�*?�Erm�{����_o��������a�����r0�������nqG4�N����p,�p�v���i��bz�J��k�[7>��!�6!t��U/�j����+;��o;K��q�+z��(omr�5]�Z]?k ��?b�2�fw�q������u�������e|��|���������G�4>���aR�7g1���]����79X��h���������`��t�������&P��ow�u�����3�YV�\����d2�{'d��|D�����_?�����7��
��y����8�a��o�_Y�(��FYnm
�����(Kw	Ag����o*�a�{�|X�[��!����[����	���?<�z��=���#cO�o�K}�f������M�=R��?���vc��m��� ;�~
��o����W������W;�F�M������=�/k]g��+w��S,���y��6�^�l]��c��x�����CL�'����`����)��y�h��C����M,�<�H�	�|�`����~���>�_��b~x��(U���9I���m#�3��v����1O��+���Y�8^�-��X}�����!�o����;,�����R\��Q�����{�6���J� ��y��=�>��C�g����8�����l��+���;"�J�X�{��y�?�FXw����-C���M�g/��T����vC�ms��X_����7�_������z�x�vt�V����_��w�76������ /�O!����U������������_������nf�>��w`��[��iX��Gd[��m�P5��?����}�e6�6����[���g��7�[�r���i����3���V�1hj/vbmS���>���M�����o������Ea�L�#���n�r�O���sI����]
nl)��H���w���x���u]�����;p���7U��{j�O!�����������z/b0��5���q,�����Q��jG%�K���{{�]T�
�d��u�	��o���"��`w�r��#0�-���7<%L�B(z��%9�R �E�\G,.T� ��>$�rw5���!Scz�8���1|L����	�az���p���>�bI���~=#���n�J�o���2�x_X3�'��@�����d����������s~�������%��\~�����R�7���ng��>&�������%v>�O_��������/���_=1=��^^�i���O]s��������v7��Y��|�ev���z���"ZK�����)o�{t��!�n�xh1�x�b�7=�h�t�����o��-cv 2ch����h�g!��A�Fwp<�^���>.)����84�	�i�uh�%��a��h.g�V?���:�J�~kmf�
�k�z9�S������.�.��^]7��h���J�!r.���7��j�r9/�U��Yw��������;�����
��7��;^s����[��w>��-��0]ci�P`��$^p�w��L�����> ��V����{����16���|��n�2���[�����
-����G�����=P��Iv��P�/x��i�w���I�k
x�������/1��q��*����k�������f��	s�����O&���*���2���i�>��~��S��4OI�H/�'��&�v�������yg��4�{�&����Y����ZB>P.��n���j7�1���%R�)�P 2b�����<X������~�]i&�&O��y|���w��BL���8���<���Z"�'J�j8c�!N#���E����N#��-BQ���7��Yhi�r��$��Z^�xO�b�_�Z^�\?�2����FR�a�=���S��
���hs�Jj�~�Cb�LA���P����|s'&�f�_�n���>[�F�2h����|~q�`��@;����d+�8;7P�K��`�`/z�B���R��zP�������h4?��~�W�t����{I����]���m���m��M��s��@Y�f0��\����XR�<p;K]�����j�Yh#����L/�����w"P���?N>�z;�����bg~7;��v���������I6>;;���O}�i	~$�9�����k��7����W;{�_}���P����������t���0���t�#�z�E5����	���R��Fg���������~���p]���v�>i��>����;��p�������y��F�d�@�$K�i�C^�����K�v�(!���5?�������G/�qRf��{@K���m�C���6�`�0ee)�?\DK!���6��;�n�w.�Z�����Z�����d�J04�u|��W���R��.���4&��h������_1�o�w{��9�XRE�E'��A���-����MWoz����3�)���x��ng�XO	�l{�v�IN{vb�C��G���,��+XS!p��I��r:����=Wn�&^��W��Q����zs}6��w�o �Z���E{�C�[
��h	��U��i���{����a\���'/���6S�������r1���?�;'�f��N��D�=�����s��GJ�@`�<HF�`�?se���H��yw|r��a��q���������J���r|�
5�*��*��������JA�v��w�y?;���0?�����"�z�����x1=�v��{L��������<�X����M!�5��&�k�||o�g_q����0Z�W��_'�������m�0E�.7��I�����������z{���s~�F���]���[@U�=�~�&0�x��}X�#�����a�%=*�t�
1����\w��J��?/����w/}�V�Q@WM�0B[~p���������nZ@���8��,g���hOm���E���H��2����E^M�/o
��<�����������B&���/��x�?~��$�w�MdY�|+;>�������K�_��7�]�_��-}�e6 ��p��$hM�-������������^<��;�v�=}�������(&���}�������������$w&T��'j���.���}���W
^�C�;���b��mO�Oh"���En�3���8��8>^������?��������������p�Xd8'w�7�{�����ey�]\��GIZ��������(4���+��5�-9���n��������P�}JW���m!
���%i����p>?�������+�i{>�f�����M��v�fo�cr��Z�0���[wz����e������z�b������>�k��[��$kp���C�#�k�`����&���w7?���0��?�C�aI��2	c��d�i�Y�V2��\W��R����{c��0���&�"����v	 �X&�p�r���X%gpq���_�Y�A����H�_�+�l�}������i��7o�=y�6x�^p�&]	[�yX�
3�Kf��m���<<���������2�:EH�� R�&��Wv.~�.�m�����f�\����)��dN�u�����~����\"�?\�ug��J�H������+�i�/��`3�\6;��N��^m�}xl���Bzg%&�1�����%O�������
���fZ�)N^�FD�����?�,+���W�|�������l�7�7@��O�CI��Y��~�.���g|/CH>�F��L���#��(���#k!��y8Zj�g�.�MP��o������.����|<��qO�����V����HW��7����\��Y`�O�����O�A��/�q.#{����6��t��,oH�u��{�
����Y���q[���#�d��6k����;�b>����.��Ix�i0� P��`|j��l���P�����-�&���K��V8�MdPB��~1
#2��7$O��,�����r�d
j��G�"�)z��z���/	w�����q���bn��=!����YfH�
{�>�����`����;m�g���5��y�W�|py^w(N<�Z~�?�������W�3d���=x�����Dsj�T��[ib.�@��J-X�!���o��L�u�u��������+�����5��W�b���<�����R�n������y ����}v�<�9�y��Q�������+_bs�����HO��������tfF������n���ds2��mo��������=��J�1��N�m���5����8��`�����G=���H?d����^0�&����s�W��;�R<�8��TU��]�=
���YJ�1���������;�g
�G$jR��&X��x|��}���Q�5*�'{�
���C�g�\-~�J���JA,+�2mk���|��<��v����G�t6�����
�m6����x��Q����I8#:0 5�j��(�yx/Q<c#��d�>&&	�u�����^���9�������*��"n��������h� YL��6�E@�����d ��x���g�\��a[7x����=���8����q����I��p\s8)��F�j�����"jTB�����H���j�����BWw�
K����6"�������?�*.$y����_�$^#�/�����R���+���^r?����|g5>p�d�%����e8#
�oj;"�\/������vb�������l{eSL�S}lT��Y_J�:��'�^��<�}[�����:X�K��t8��r�VH����W]���"���=���O3�v��L����L��@iD�}����u���$�������|EV�e\!���i]������[����:���*�x�J@�����$���������
<��Z��u�*�]�!*Dh�6��g�~��+��'�?�G��b����g���z?lm���\_�ri���������T�y�Q��x0���$e��[��o�_��>>�����
9`E��}� ��A�uyUo�nAN�%�s~zv��=
��o���@��}?��a���������p�`��`�;��K��s���l/
$8���f����)C���a4\������K8{<��0���w6>�O�'��
�$V
�����������������f���U(!��x���~�j�q�&�e��a�H#4d���^' ,G���PRzw.�+�=|�������,~o�m�����7�lR���]���s����P�$G��P�?��5�kO�Ulc/V�X����g�6����Aw�y)���q������
?r��e9e���*-n7�Z|����~����5.�Z��.�t��7.\��M��[K�M�oN�m��D��x:�X���.]���xOe�9��-�3��dXg��/��������)�wn�.���~�����W}���M�o����&�k?���1�}4#�B��R���������bv�K3G�N��W�
3�p�0^,+r)�$�,�ZJ3���t����n�zk�����K'�dw.�yr�]����O!Uk�fvB8��?~������Mc��	�����	&Q���[$
<��=�g%�d�~�L�����\T�������lU�66�	?&���:�g�����n��ss��k25�C�P��U�B
�]��3���1I�
�Y�7�Ip���(�,��Z�������G*!�F�1���L)9a~��6WSnV��J�T�0i8Z!.����)��d����i�K���}|�����;,������a��;���m����~[*hy�a3�=�"�9Hr��Q��{?&$�������!�;��q�_i3��W��G���Ga�L`��l)�o&���=��4�E&�
c���KQ�(/�q���������
"i�������)x}�����M�����S�f�������7�X����\�����~i>��b���*�t���z,�a'���[��Z��Az����K����f��;��Y�v\�p)���<�e�Vy����,�>��R��+$k�LoB�~�+C9<>$���g?D���K��k2�,��?���W�K��&�����3��;um ��������������7<i������i��Z����}�x��'�����yA���u�#t������*M����������R�����������dvz�~���������Z?���h��Ha!^���>��������w%���\�����:���
�0���/<~�T��������k�
��k�:������������J[���
�N���	�
u
�/+q��*�����N������^�������@M@T�NE	�y�!�bE_�C�z-y
>"J�?���t�����b��E�6����������'t�)����,�j�?��:�(�xKW�m����g�5M��m��G��7*���������5M�Z4m�������y����Gv�u�z^����Z��:^/[�MN�����}�����W��=�*��yi��]�G��(�Ul������F��U.\/����Y�/������h�v���.�����q��~PC���F���i��;�5*>���lp]��^�vE.�[����w1��������MW���m������u�m5\��>h�"LS��gW��=
{���	q9����������:��R��oR�G[�D8��2qr^�������#��cbV3`��NxnW�s���n�H�M��Bz�g^���.��WrQ7���
X��08?o9��a������n���^�h�x_^�~�������~V^|FqT�����Qn��F<��z[�������+��	@S��=�1|,+{kI�+�����8y5�iw^��}yk������R3��F�6��:���]\�,��1��z��q�Z�%���Jg�S���b�����_5q�;{�����L��l�0��Jg�W
:�_Y2�%�������d�G��w,���P��5R���+X����F�
���X�#�u�}�e�D��t���[mm�*[�������rS�6k8>�O�iv���
(��\�xU6��W�Q	�f���B��1��>QQ�M�R��9m��g�1k����ct�k���F�
������k[�AWE�(�������i���^�Ls�����Ic�'�C�+�0~����^H���J{��b|je������l�����S�x[^�s6�em��9��6���(^g*���������H�_4%���2�-� �Q�/V�a��gK�:���D�u�]e�UW��:4h�E�i�LUi�[rm[|��:��-��,f[�m�H�*���v&������y)��c�g�R�NY��:�E�l9[P��4]�:4����P/M�r����s��M���B
�pl�$������h��b��ml}�����;������{�(Ze����1a*��*�����s&	
V�k*�L�Q�� Z��Z��ku68A�����`����r������w�]�r�m6�R�5��b4��j���M�j14L9K0���1o�;[�G���I�����%`3m�>�m|����!�u�e�-�[����yt���=�ul/��5o12�_1�l��m"
��tA����Z�k�A�9&��w
FS��6�d����#�t+���S^��K56(������G��X/u��-[��-�=*�*X���
i�V����&YCqjF�N��MS3��l������3lF�_{�JF{�j������c[6~8�6�-��mpF��>N���D��1�'�}Kk��?��*���u��
{>jQN���B�q|��@�u�5L5��\1�*�e�r��Qr��8
�g�b������k����mV[6cVw�,��i�9��m+E��4������:P>m�6��Z�
��U��p�0vx�%�FS���uY%�1*M��M�1X�/�������(��4b&rV�To��@\�mO�t!]��Tuf��aV��pWu�S#Vp���h�S��c�����k��Rs���Z��V�S3	�g��4D<L~\�i�u�n�����a��[V��nm�[Y+%6d�o��w��l�%�3�Y���;���F�0�����n��������4X�.9�]��:&�t���em�N���IgQ����0�l��
,������J���xcgv6�e�aSIq���w�N��(�Q�������Q��0��@�b�y;�^��hk���
��,��5���i��E^;v�`������'c�����_n��
L�N�Ce�i��\"�M�k�vT�)�p:Y6�*��D�����L� #q�����gD��;�7��h��F���Y���.�a0�Q�EQ�Ak05����E���,����Iw���%_�x��t->!�S^�,�\�S���hy�����mG�\�wI�\s{/��\��G��@���o��I���l���c+��yu��f�kF�����2|�b���-a��!y���1����y���W�RC�Kp���Vl�m���Z��H�M[#�E�@��^[&]B~��i���-G�������WP�Z^$�P���b����+������Z�%j�G��]�Q= q��t	�
MHW0u������1���	!*��������*xq&C)��q�(Qn`A	X�*�e�w��
���d����V5F�k��[FB:���d5*�)�W�sA�;��XZe��`It����`dD��f�������D��e,���r���\���@��������d��r����`;���U���
C��������}f��@�U�i�3����`���%�u�H��R^��k���n��uH8� ������A�L�5/����)����s�F�.x6��0���	bZ@c-c�&?�k��9���]�t�1��-.Q[ �
�[�=l�-��R�?x�hI���"��s��U��E������akq%j�;��&�F���f_��r�N�`0K������Q%�<���B#�U�/�����(t4#GU�n~�l;�q�l��|&�q���,�������*�9�`���'��J��)��Q��.����e�M`Y�g�x��9�z5�������t��o1de�E��u&)�\�����TU2h����
q��,6�,�t���QE���%�������b�d����mWJ�0nk��5FM�E$����-w&O-�v�9����/�g���R�CV4��u���<����E*q�$*�t��E�:��Na����6,��\�8#[�m[����%���
�TGK�e	:�]�)S��@�b�+y����������WRm��b���;TZ��ns����lnG��
4��#H��%^E��0;/#�K)xz�Lc��RL9K�`[r���M���b��
vXa���("
�����l���K�k�'�A%[�\��mg���vd��^�(VM��h@�V��#����qM�$G��Bft�bc2�Ab_n���?G�j����9aJ����_+��*b����@�;y�6
A��;���i
:9 -vDA�O�,����e�j���U0W X�	�����N�o+�9l�&���q��&-���<0����=����:E��rBmM�3��b�pG0z,r�-
��c|������;>���$g�f�!B��q�j<��K^��g��f���g�L�/���(��P��6��f`
�t[E����������	���m����c|<!L�� =�#��8��-�m���i�g:Zva�����#�Nh,�K5�g
,+�����"�u�/Lj��n�Jp(��Z9,�P8-A��:��p���Q�x�
9�	`�����NX�	���l�����V`q�Zf
vp
*����t�������&0��kc�^b�DE�=����pb��X��p��%���Q�Jl��@�
"S�`�
3`WpP�j�H�M0���u��Nv��$��@Y3+D
3(G�s��6q�j�'�1�
�)�"�f��������h����a�	a;|�<@A���5�)5�B�~�������c��m
�f��t;��hD�MqR�@��������>��c3��V�!Q�U��k��
��a;*q�;=���J$������q��A�?�M��R��d��{��'���-�������
.�B(�F0�w��
�5MB����D���a��H�K�
�e/���`��A'��a��t)r����@�T9$�����q��tl�-AF'{���1k��/��� ����W]%
�W�#�[w�b���jSH���[
��)��s�1X�`&X�dVN;X"q�F�������&[P������D����T	����H{��D�����xQ���l�M���p<����@��
�-V[�\	BDx�E#s���:�?�`'��N�t���F[���r"�x�#q��-�NI_+��*:�G���a���b[�'[(����%9� <�NtI0 �"����Z�@���(F����D�[��"!���A�a�@	a�a��$�^�r���"��j��k8�0��Ep��U��Yy�R����nD3!�V��
L�'hf�����:�3X�5���T$�Y
]Wx��H`��Yv������!�
�i����LkAq��ca�u���`��v&c���%�
�p��Qw��S@�!�pq�k�j�d#��#�Z+����]r�N�$�?p�pC����==�>��O$q1���F��f5���,J�������&�X����u]C�,����k
v�FL<%- :-�{��lR��F�c���l��D��
�X�f[�>-A�g�xl��x����}����a�b�V�m�,L���e��ba���R
��T���='�T�G+BDC$�_&���:1*�3��q�m��C/l����-%�TU�\c���BJ����<���%���-+"C%��\�"�\gs��4��K�"��.���Jm�5X4R�K�t&VP&GS���z���&J�����k"�\�D�(�6���e�wd��8�9�/�'	�p�K
-�� �<���5�D�f:�ke.��M�CSqBMr!����r��R�U	p���P�0�
b����7��N1.[_���h��Q�D��A_���C���o�;����^@W*P�������2�����\9(3,�a����3�v�Z�
xsu��%
�7FhRL�".�y#�Z��R�p7���6b�NL�2�4�x�!����xB�9�[f)!M���@�i}(//������[��Y�Ui+�$�����:�!1�[�M
Y\�Ud&2�c���I'5����T,���e�E��PA���]��c+���Pkj@�NE+Vl��������$��Q�0�r$�i�`�[ED�"$/�|��A���[,m���D3
Z�����{�#���UM����N$�����xW��YFo}1X7�dG!�`����*��&�j6��&�B����D���e%���K��W��g1��B�%�Aqn�9�����)X>+C8>vc���z��V�����-��Mrx�94�FF�u)4��q
j0	9����\�9)t��uy1J��qdL�;'fA��&q-U��*����@�G����4���0�I��8B>��\%s� \����)�n�/;���0�Qpt�����vS���T�[�@�,#�k�C��0o�WN��(W��
C=��tv�B%z�%��Aw����4�V�z�����;���	7���*'���3�D��@K"e4�BU��O�,�889�VCd�R\�H�F����P�<Ssz�<��A��X`@�N���E��Sum�6��>8e��p]���\�����M(����G"��J�����&������N��N���~JqV�8��k�!�E�����^Uk�b0��_X���#��&|Ut��Lj5�4�jlp7^]��j�*�P�@3������-�o�L�,��0
��(ZU��r \��������2�},���Q�WZ������R��`V��DI�cU��+nW��y$��O ����m*������@��2�����/>�%����Ya:��������n-�~���O�X�����"�� �������H\G c�W)jg1q9�$�5������:�8��I�Z�(fN����`���C	��!��S�,T7�Fd���yS��1����v�B;`�(�e�
}�3��.d�RB*���S��V\=�Q�u��������&�=�;|�4�6��+�!Q�+y	i>���C1�MB��]�)�]�2�T4$~���DHv�*�r�ml��^��"��������2�m����?uO
�]W��7���)a�R������C�zH��:�bu����+���	4V����G����?�se!T�^��ELT���R  ����J��kb����~��B�Ji���@�Vb�/b ���|��".����&X�PDs��"*����^���k�L���u��4����J�PRS)
+_)�
po�$	%2�r�q);��x�&Cc(���q��LB�{T��E�~��\�UL�&v�����*����QW�-�B�����{Bj9��5P|��g
����U�F����P�!����P�"2t��$3�	�D�0�Dy�*�G������%w��7En`*�7�8Y<T���m���S�%��6+�R#���GC�h��1�0g���se��|����&�����3_�%ed��R^V+��-��$�f��+%�L�~���(3Q��`tlQ���3��w0!��(����9�\�`>�-�1����E'��L�E�
.�|#�A,j�=&Q�z��%�V���!i�����������nvI��!�[QS������B�}*�uB����8A\�f����Z�$:�U�d���%��������=M-w��v��Ha����"�%�(�Oi(p��2��jp�[�?D�j��J�oN��W&0b?QEP���p��g��$\��wH��/j
g(s�,������\��2#���B��
%C��@H�Z�����*�b������VT.�d�'����cR�	M�$o��b��Ivq�T_�������gzY(��"/���B4�%��%�2��������kN��B���G��d�4����S�w�f=�^��a�hz������>��HZR��9��cp��J\+�������`�u�p���b�"�ok�������)%&
��\�Z�U��`�	���Q�"�#+�
��V�) ����2W�
�*shTt!�Z�����3���}�����@d�s�� J/���NY��T)���j0�T�����:�sd�r��	��#y���+���>��Z68���h��)�,��	VtbL`�������
��:�m�Vu&t�V��9��%�����������n���'��!(���7��i{��$������J;D�D|Tj�}L��U�3�>I.����/�����dQ
��}��-��^�j�X���*!�SE�r�+"���Tt���c��XH��U1.�F>rD�l���@DQ*����\�_�Z����_U	�!��YU��A,^c�b��_��i_�@^r���Xe�0.U�"�	�MdoF*S6�f��
��tiQ���(eT[�E�2�j���QM�\���FE#s�	e����*�����VBN1LRv#`�N���UH�d*���$�Pd*�Fs���J[���X��?���Q�|�0�0!G��(���84b0�������Pb`��B�RKFtQ��b����fp5��F�u�^�0a+�����R�� {������2�y�D,P�]�K�y
���%���+�����RB�(E�
� ��8���F�PI��*�(��h-
��u;RI
�H��
��/�^u�*6M_��X�S�XCyp�����(X1�5>$i;�@�+�yX8B�r]����@��-�����U-%�+e�9YT�Jc��C����=A|N�mD�W@��R�����Q��Rz(5Tq��Ru!U��x$>o��C�P��� ��%�")T��PV�@� )�*�fT�����@���%5�r*�Z�Q�f-
��n�M�z�o��L�:�%$N�[����N�0�`8�	j
31�O����BY�D�����`=7p_s�d����,�$�r(zo�vdD@���D�.c�Z@�;��m��c����0����j�����)y�N�w��/�
b�L�%b$���S�����P��h{�Q9���2�]B���K�������\0�<y�p�Q�9dq����(��R��F<�>��@i%f�]��hJ=�|�rj"?"#���E��U�^`T�`R4����[(H�j�"�7bD�T!�|b�=�c����S��	���DJvK����;S�~��6J���&*NE��T�������mA��jIQ��c�
2��2i^��UN����_�������	(�`
F�a���/���1�<�`X��q�k�[��
�AC����A��%��x�����V
�&�L8p�4��T�j�|���������=U*w���ER������N��~�<���eDT=;"#T��k�3c�6��[��\�]�)�H�z������m����A6�#���-���B':��e�b5�Qo#��<.;Z��t��UA-��`���t�g����9KH��UZ�N
��J��B���K�� ��b�J�Fo���.�����yxpa�Z��WHO��S0�-��NM��#zY+�����L�����m��4�4��dk�T�_�Q�i�W
�h�}^I��h�}����5���l�0S��{j���'�'V�;���A+���i�����N�t���� ���e�Z�{Z��O~�
��%�M���YM@ByX:��Q uAGq���ap���|7�U5�"�bG���Pe@`yI��IQ��I�s���������^�H\��W�&5�P)G��������i!9�G��*Ro��~�EU�%\�&��q=�0�\��W�����0�Si��e�������* ��I'{8�ekU�[����iT
D�c��	"SD/UX��{�j���WDrU]�����@�e)��t�K4$8�&M����*4�e)=���	��{����1qV���r�*
�����w#�D?��W�����$����g8���#���j�S���Q������P
E�GAp*ZC���
L����)�D���o���{�x����F�B	5T��[m���mS4��z
�jM��6��Yy�d����:����P��*���o�@�N%����Z�?�]C-P���f ,�b�2U9����@�P�JU�&�V9���fx�%:���#%	G(%�P�(�T��>p��U
x�Q� �6����Q���qbP`��y�����y�:!�&�����8a��RU�!7t
��$
�@�_8�(�P�p2I��J(9���^��rA��`r|�lC��8tM��D�H8���S�@5+�/�����nU�}A�cU�mT��R�Wbe�����`���bm$�T�F��J�)�V��$�R��8b�)���P+��pQgB���-���<��
��(���X��"c��cU�52���3��D[U��^E�;�s����Ju�����J���Y��w����*���xE#�!��4�}�.{j�������Tb����!�H�=����;���d�3�H���Ze[(�G�Ql����*��PUb"+#&��*�LY|����PF�R��`��y-��/�]��z��6D�:^���Y����"���|������/���%_�b��u�����u�)7J��0e$T�4�_�`�*�Z�!�VQ2���l����^�sn��:c[V���.%r����J�S�)�kJ�a�t@GRPKX ���Q�~��>��Fd��0cH��D�i���(6�W\�6:��	�F�J)
��RD����A�S�Lq��f�d���'G�)t���$��I������@Q��\�h�FE�}:�J����Q�VF�Z��SR�xF*uC2u��TQ�N!0@�]���c����-���0J����M����j�Vb�S��W!���`����#I�#�Q�<<LsUZ��,�G.�+I�l��X�(G�OjR|�,��M�tz��@�TG�8��v+���:���������h�%g��n���N`	
�~A/���`�h���B�:'P���NB�9�k� :P�:f��A���t��;�����v0��`��d}����RET���{d��r�\"�)�b���W��(��@g����Dkr��#*@D|H��:MM���
s��=��Q�fo���}{�d�U��L!Z�R�+@�5��l+�1�\�z����m*I�&oh�U����Xt����q��J��)��SX/:
O0�ld�������u��c�P\��sj�%��K�c���T�^������R�c��u��r���DF-�G+���?	���VUm�
e	0�*�J+:~j�%��6�Vt��h��yTcS����8"e��C����
��Rx�q�����
z	�?����&���6(�#��?A�$W�^o��ir���~E(��������\	@"���Pz�A6���'
����*D3���v:�1�����XN�����(��6U���Z�����R^�����)()^'{�d�heK����c�sPpc����U�����Vo�CC��f�x
���E��������C��E����,�G�F%9,�B��JkF����Hh�k�X�a�mk1	{�I2�J~��3��p�N�l.L�V�>6
����K�i\��Ehm����b�?���'������.No��K~����u����:��dR�Ua����������U�����%�H
���6.�<a�'f�G���"�VE�)�T��qAI�J�����
���a���yxN�6=�x(�,\I�t�
_&G�&v�Q����QGI��c�#T�Uy�X����(1"��$Q�kbTW���I������&bc�����J�P)��N�����0��Dv_]�Xa�"�;p��k^���(Ne�K�f��D�C�J��N�H������J��V�JW�-k878
�]���p�]:����k����M�)8+�^�6��\���Rd��i(��j-x���
�������*%eM�jbx��Y�O�:�Tx�V'������I9�{Kq�F�t���������Z�������Hi���G.m��+��t@
0~E�y)������L�e*���x��A+1��`(m_RB�N�#��e�
������Gr�V�7��^��bqZSJQ�4��V���U�X�^��� �%g��3JH��>!�&_��>��f&1QeRn��`5 ���@�!>W}>��[&R������b���\G���*��ZG�:��9��**��JI�(���cxqp���T���ho��]���t����;,B�
���2��11��BG�3w��
����7�Tm`Ve��d��j�����D�Pea��%�$�BZ�Q��R*���J'i1�)�MU+6T�g+Nz�=�&iB�5�5RK:|a
y�Bd�~L�HF��E)��(}�!U*�NxU^�*r)P!`���`�*{A9J*n���<��J�<�Y�n#�����A���DT.9�,�kDPJ�"3D�r��Z!TqT�7:�,x�HOr^tnj�����K���p]+�("������T��j��>��
e?jo`��H��W*����N�
��T�}0R�s�$;Z��1�,lT
�_����p���������b5u����h�+e�����G{���l7Q1����/_&h�4�����
+KX�e{�6�<���_��}����H��IJwlU��J��aq8"�wD�k��*p�t��\W���J0����p�U���.Ql�m��W���48����u��M�:��$<��[�����rTI
�����y��IW0��0�R��V��]����u�����/t/�8*���r��Zg?��J&D��o��0AUW�P5zj@[����Pd�t8T���(�����K�q�(U��I��T�e������{:���!�3�E��iR�?��OH�l[�bt:�Ng$�QW=A�(z-����%Tk�/�U�[iD����11r���a%�l:W��Dq*��|��HQTh������7����
O8�
�����U*Y4J���("($�^iE���s�:��>s,):��p�{1�U%O����BhN��T�7��:=����k���9���9K������u�T���d�ZIQ���V��P�7��l;H;�
�tLRhR�.��R]������Rzgar����j�lv:��Zt�Q��E���<@�:I����L�*g:�)@�V�������>K'��])��|�R���@�1)�C�1QUM���TBiAD9D�L�FYW�EN��r�C���T����(0Lbz��rb-�����*��+��l)�lQ=Rp�����*m/4�P���f�&�����B1�NH	�������U�>'�N�9��P��2YC���)+�q�a�A����(��
�p���Q
�<����D��Vvi���&�#S{��d(qy��<p��l�H��5��W��g��
���:�4�V�2���}Dh�C_)���pSR	��g[���x���igHia�85pH:��S&��**�@dY��{ �)Z���_�.��0"O
0
�D�+��ur|� ��*&�Bi b�3��j�	 ��k����������oa��Td'F��5�Q�D�����9�Mj
m��V�����s��TJ���`�@2A��<IQ�&W62��)�N.(�R�� w����&�X�}C�\
g*��@��##�����*�st���yJ�1��p�D�)!�X'��*0d���B&��3���}����#��U���~�S�`�P����\]|�F0L
J`�������4"�� U\W6��Z�$��e�,����#���1t�{���8�z"4��5����r��5N�	�M�	)!B�S�R��
�B��5�X����m�,��W)���1�d��P�u�x����"�+D-��/&��S��K�Ch)�@i�:fv�9R5(��(a��jh�S�Lc��F�U+��J�w:�2;e�J�P�+������G_u|��
%��GEF
u�FE�r'�H�d����y�k����
7d��h��C����{U�����DVE�:2�W�4��FQ�Y[dDs��*V��2�D��0AB�3*��G.S�U2�L�1�d�e(%�O}���#iHX������a��$3�B��<R)f����e����0?CU�t������q�	D���!@���T�[Y�3��\�L�����'���vb"|\��M���f�"j������8h�jKx&�R,o6#E�������5P��k:��/U��Y��z���/"���Q�
���@e�K���f6�\�x��J�5����-0	����S�.er�NV����C
c&S7���oCUQVP��R1�TX��"�����Rq���P��`Y������^8~�h'oQ��K�n��C�d��0��JG|���0'�5�
`����RT�s�A)t��
r�X�I��$���k��b���[�[o(BMik��)��b�(p�
�f^���J�+f��|����u�+`�U\�:N
kj4���A�$j�z��Ye�vb��t9V�(�E�����)i�U�J9�P�����p��N���X��A�r:�,�['�YI�E)�"��X����W��Y}=��\��)�����0��R���t���o�5��mp�y���hW��a�#�.r]$��Q���P�R�9�:�����v��Ui7b���*�_�+Ng���t\����I�N%`>��b�t:�wHD����x����]Ka�`/:����%*��%5b���J�Ij�A�
��:7��(_�q�J@��A����OCy`����K�#�U�RDN�sBj^��D�l?��-?u���*�_;4�O����� �*���%:Q�E�W�����)X�Bg�*"���
������P�
�`�Nq����*4vH����p�4��� Q(a��p�:������6�����:e���on%=rF���� dR�U�������CP�]���8��x_^�u���������p�$
�;�M�<�VO������
�$/[f���u�qU�Q��<�V�e�pS}v ���:�S/9�UwU�VOs�*g1D��w�7�7�<�Z�P�%�*�
Q�b74*�bB������eQk����91Ai[���)~�����un�b�Y���j���H��L�� }�}Q� �Ve��&��������Jj)�_��O5�m���c�����:�-W�~����5���&5�r�+9�=�^�*��q�#pYk]�v!�c�s��p���U���U������]�J�K��a`����2@-��r���������Q���
&��3�B�XiJ�&+?"��^6-��y�b�R����G�����m!/���U��VG��HY�*�D��Y�&�D��8`�
CT5f��I-Z����&�G���+�,`�C�j�s��5���R��QM@�tU�jt����:
�m��R%?+�@5\��g{0�_�I���R���	��t�XzA�&'�,�����*��T�I��������t2���"c_6��U�P-��t(��!t�[E�$,�T�Kyl.T
mU"O��^Bc����";�\���B������H� �Qa(������/�)��������*��.�?��S�@�� %����Q��B������S�T�p�RtZ�-(v:$��q��L��xI��H�s�
�"�5�i����jHf���H<b�h����y`�V�PV�%\��l�Qa`�{�+������Q��c��Z��f�"\
>�e�Nt)1i0X	�c� w�B�0W���v�`geg�9E��t��4��"A���[9���j��cE������\g2��:�kp���f�����Y6���DEuVYA[�^J��B����2�����R�z����O�vg+k�r8%5����n���B�Ee�i���7m�
��	���P}����5���ZF�)0�*uZ�-UbIA�\'��o�@kG��i�
��C�f��T�8��)(!�jt�%�<\�[��������eF��Mf?��#�*QUl�\���kSI�R5���7P�l$�x���>;>��Hm#XT��*p�\5BD�V�`
,��������QgPN��7���0)��H0J0���uF$����M#U���W��:WM�^$a�ku�
p;�#��������8y5,�Jg��U�����k|p�!��x3���C�����^������z��AT�ib���LP4N�^ZU��0-�MB��8��D��9B�(�W�4�2D�W��^J^'D�tV��t��H�:�����tX���4��NR!A���T���~�<�G���$������U�o�Lm�1,�}!��AL9� a��x^�.��&fa��#M"��u�
DoEi%��T��
_:�P$Q�1j����_�m�B:�%���cB�������k��J��Q�x��tb�T�r�eD��@����S�T/��t����F�)����@=N�v��.m8��J��HS1�����h�����t~����{�"V:�H5����+�B$T������V'��d��K�p-5���8"��x��5�=BNZ@�y�'c!�q���02!���i**����*q;t!�\\�e,�@a�Z`����9%E,�\����*]#i��QRP�B	[:(91��<Ah�����GV,������kv��|F�D'�	E@��d��P	�Jd6�'8���poD���RQ��� �-�n��8zJ�u�AVqR�(���d���4�
��Hp��8�y�+'��,ra�Be
���h
��C��i�����T��Q�L�'������%,�����EQ����
mu���;���b��T�B��nS�
�s����WUJ��������x����jK�N
��Q]*Op��I*�u����f�tKo��@\�����0>HS����U�����1s���WP��I�B<;�*�V�Qv��N������8 �,�W�Mp�FIg�E%5�+����/����RV�s��:�P'4e���hP�g�hJ*b!0z��VPl��WD�a���`�t��#4��S��S�21,�VpL>d��]O�j���PP.���sj1���8:/��/�h�CtI�D
����������T%���i������bH����B�S�<��\cp���
���bQ�:��#i����(vD�s�xgQ��]P$��|50�D�����cW+�@�*�E9/M��`�����=�4v�R\���4����T+H2��RU`�r��I1-EL���Nc�V*��,uUC#A'W�Zxl)9��Rp~����:�N����-C�����>B�S���:��d���
�Ho�����h�"�� d���`@m�S*��2����� �:���)t@��NFXQ���Pb�T�A�J��
��`'�j���j���R���SV��*\q,�F9�#�w>C^I��M��yBP��T�VI��W{��kWqe���+�������%BL�@7�N�j�e�1m5�	8
����U�7�>������!��9��Z���c�1(XX���:i ������� o���D�\���3�����6�,h(Q��(u'z��#Oq�E�L�����2���T�p�Mj*�y��	����@A8!PH&��Y�<��P_�9�|���
�a�fQ8���Q�0F��J��r^� �;����Cl�b6T������W�%�Y��]�0;B����J��jQ^��%F�c���yu�i�r��6�p;{�(�eq�7��YB� %F��,C�B����3
R\��Z��0)��X���nK6���?�tO����T������������9so���wP������
���t�������y�t���8�1yz���Y�':D�n� G����a���3����P�a*�kTKN_GjWYM�yt�o�z4B�y\N��t�f��ag;��9��i*#f����%2��o�(��J��AS�LXE��|��_�����W3�\[�_O�������uG����s��2y��BS�������BG����
$��(QZ9�������X�{ag{����^&����l�� S�6��L����1�5�C������a���m��_n��8���5
5�#�z��d��vG�"����n>�I�����8G���SNzzq��.��Q	zK7.�59������M�[�]�-)��(8�M���w>���������o6���8��'�j�F�Cc��D��Ty2�u�������������C����F���Vv��'�,Zq1Ob�	����9�=������}\<���x�zJ�Q��s>&��%[@���h%��JW��1����"G��u;�W>����#�[L-��������)'��L���1�9'b�H�H����+�P9��������
�ck���>��`�����Nc��@���+����e��kzQ�5����h�W"	�Ui��VV$���`e�M�eod�/O��������s�3si�w���+�J[���=t���l�Eu�g�;:Jm����B�k=�ja����,*�*�'n�j�����Ur��-}4�a*"���
�I)SJ%O��k"����������J�����P�+*��Ys�l���5�u���c�fa���u�K�k��]1�=��n,�����Zj���*#o��G��#VG�=�q�&�+�mm� #�����6�	�O�`����s���=��VDU�@z"����+�N���{�	�,�0nUo�&��T�mDa�eE0��w�T�\�F��;����e���Q:��]��p%�~M�Y'��f��J�L���i;�l�� [����i!����� ;bS�z������9/�@�NJI�(�ua"�������4�n��'w�>������Fi���V��Ep��'��9�W���)�5�����{�����35��5���8��A	g9$[����������w�j`7d?h�q����	����)
%c���e��$�����%��^�	J��1��:�OQ�TZ�*��E��x��d�R���x���(b ��[9���09�Gl�� ����Q�#p����m!�\��a��vtNdf�D����YXxV!�� ����HJ3��}t�5�|�#oL�C+����I�oO1	7�	5Y�3fcRT�k;5c��|��~DE�GK�!D����
-O����G(/������v*���M��3�n�b��!U�E�/]EH0�gm	��;�����6#���*��0c��%����#��3�������c���jd����77-�2�"�e��@-��l�R��4�S�%`%� �JG�����#����^���0�Rj�M����&�3H2J���eOC����� v�0�$�~5�l��%i��Z� �v�)��xUL#���E�0M#k���K��S��!e`2����0|H�8�LtT
�=L�U4�A����N�y�����@�f4�<p��F�������������0h��>GZ��*��X�Z�,����j�#Ee�OY���T�p�!��J�x�Y�����s��R���3;���P�������@�p4�9E�W�G!h9*�
�VO&���:����bF&bF�G���Ioe���4nA���!&I1��7W��#��

�@,>Q�;�)��S��C��H����S���m�|�?dT��-��i�������~��fT��{�qR��g�E������#�����0*sDy����h.Q)��f�b��6W[�j11WLZ�r�n��k!�#���J�&su�m�����Y.=��X�@�:��PZ$4/�,��Z�I� m������E����T�
OG�]��Jk?�z�������S���r����v�?M1���|�������$S��:���[u�v�4�eC!+(�-��Ul�*�E����������bk�p4��;�Z���7u�lxg�I#�M����s���?��,�l�I�B9;����|$��U]v/|�(�q����ka�rU�C\m�m��������8�W����9�I�����v�[5��������������d9
������kCChxh��L������J�����8>����j�e�"h�1L��:\�#��$� ���Ro�����H)���uo�@�V����"��^C+��9��(r�����d�8Pr`�:)!�TJJ� "�
����	5�H��Dq;�����y�M�JC��
�d_����N��EpV���t�Z��-���_�/+u$*U-��l����
�d
��v�,��������Fw�#Tt!��+�r���=��&R��w�d������Z_u��jn��������R��vH
���`^�9�����P�������l���,Vto�����i���W���0���q��h��:b���z������{�-������p<�%��r`�oz3�K��r$���yo�y��H����f��C�Ytg���I`V��&�_��i�#OD�2��9@G�;`�oE���Z7f���|L����'�{H	2�L�����\�
����[u!.z�1� ��	h4.������@��E�-5Q��.���b�j��T�������z�C�&��t���Ok:�P{�IuL��4te��E]����7�]j����4b�*k�� k?��$qB�	��.u���3N[������r�d��5[�;h��)��	2��<�4M�w��y7\�S�$,U�?�
:&�K���3k����P���RJ�2�2;cwx���������3/.�W�&���R�3��F0�W��hn�J��(�7):�J,���#o�����ZG�]��hU`�����c�h�\wTu����(�F�)��'�����T&t���Sc�	�5�1��nS|8�i��]��j
��;�!�tb$���L �B��9Z���5�f3�8�n4�b��f��:�b'�����w�C^xJ���\��hlq���/���0p�S{��/S���#%Y��)J�lB���j�����$��a@-9
:�En��K��'�V-,��:�/���+3����>��XqW�(�Zc���{I��t����U�
������F�?]$�Y�D�(}��F�lF��y������xF��x$$���1a�A��8Z|L��sp������s�z��������F��3yt�1�F�0=�NL�YF"F����K�K�6�k	r��8���!�lsUw)hP>d��ip6�l��j�x\=7Y2W������T%����!wF�+�4S6�!�&i��f��]6�U5����V�&��U�ZjNo`	3��E�RS[�"��l��"�H�X��p!b��n0���/H��j�s6��Qs��1�6���1@H�������s�"�TH��M�Q��*���	�"���xkXF��6�I����������a`h�{�r'R����$d	P��s5�z�;_����c�E0����M�KH3�����~�NzGU�N'fD�8Kb�<!�7s�����l��f��9Z�Gi)����������>����v��Y8����X�Kv�6���X����;���@@��\_�a�v��9�5��8����N����[%��(�W������tH� �a��96��X��)�D���2�i�	 >��JafJ	!'��=47��d�FF9:�x.��\�m%	��w'���:�<����0�D�c�2(���E
�y=����%:5��:d�[c���������������JN4[l�r�
������"��S�iL���[v��:B����U��!_,+d�H�
��!��.('��a�C��|�b*���r�F�/��	����
���`��Y�M6���{��G�g���b��Y������������:a1><d
M���+�z���*�e�'�M���D�K�7!��F�t���";c�W��XO�!��w=0���Fs@Vg�k��!xWt(�B]hu���-�s���)��s�5roL	�w�2�bS`�mN����Ng@����-G��}���X���\��<�w�l�F�[��i�0
k���b�
19Ee���I�o��6��E���C�4YZ�E*������U�|��
��rJ/��s������������ujd��!��`���<4��w������C.c1C��5K
H`����KT$��C�F�3����3R���J�e��N���)�8�b���.)�
��RI5��C�������%N���N���uoQ���Y�,�!K���w����nH���s�l�f�ynl:�i;�o�vE�)����*����kg����(F6z�AR�/0�rv����~l1�W~Cg�����9�z����1��2���Wq�2.�u�!�jl��Z|���G��8#���63�R��
%�Dkd�����"��D{W���u�qW[G4=0'�
� #4)���������;����Q�$�L���l�f\{:J���5�4d
��F}ri�!���!�1����tb�����5�<4������30�,%�Ly����������������r����!���h�k��R���]��	���Q�W����z�����q��-���U�C���H.�O���L�����UH�@�a��FF�M{h�:J�d5Od�Fc��{�
S�o�`�.��
e�:o!005��D7!�E�6�;����:���d���J�h�R�A9a�ZW��O��`
�`G�p�X�{p�3�>������h]O��%�}���t5F�	%f<H�U�s��#�;)����;�xd��p��">a#6��6����f��wg��&`&��1F��:���M���H��D�!��Q��'F���C'V:���D�dBn0[�W>������?�pjtAL������Oe�yc���x�f9C�����3��l,`'���Jl�n3���;q ��WCSg|��t���`�d8��B��t�+D�,����a�)��Be�nH�]���j5W�7����dJ����0������)��_����Mk3L
J�P-8��R�h�Z8i{\C�q �N]�����������5/bP������&=p"��e}V48�����Q������*4Z����0��(��
�]����<=�?������8R����������a��#�h`��?KNo��F���d���2�T�{���#�bvh���V&+��~*2[�lc�3�p3���3��FF�v��J���G���-5����%�������[�GK�,�,��VZ�����9��Xe%�0-1�2q���B��H�d����;���!
V������j*?�������hT����|�����FlO�u�%�3�
+w�e�V*������Qn����h��7J4�^���yt=H�24@�du���C��(�+��I�9��,��`�`lf�sf����1�K(�9����6c�pF �����f�S��;i���
�1[T?�v���s�������xzg��K��!��������z1Yh�������(@Rz���,g�0��W��B�LA2�~�|X�P~�7Q����T���@UT�q'6�����9#W.���c����"�����x�M]�������&�������Y�8i������?����C&|(3�3�����Jx��V-SU 4t�i�;j)��k�Cr�D���	��Nav��
D�L/";�N)X�g���gzQs8=.(�Xpa���A�����A��`s��h�*�n����xq�&��SWv�&p$7Ax��M�n�=�o�j��5�Fj��;���a��k�|]���uq�9���s��F��j�g	��E���@<��("Xu0��
���Tj�(�	Q�u;�h��v���X,�������)xM���<H��L]��T�h�LW����R��C���en)�����H�Mw$����<UB����^���A��0�8���|
h Y�n������YQoLo��6�{���3 ���	b}@
/�-A�'{H���Xoo��)4T=�G��X�5����o,���d��E�$������C�V��F=*��'���B�3Ct�b����H9����=G'�@2������������n��Y�z�=P]���?�u���4�EG���#�l-v��n�ez�:E���$�1�����N�p�qG�
�����{
\�#����5���t���r�?���;��~�X�v[�r� ��S�
i�,��
A��6�%4��fr�	rC9��W	:r���
���*���8�KQ(�+-�\��*S��/����ZV�e3�������"�i�����.}�P��c�������M	����Hs0�K �{�A<�e	p9�J(�T*���R,��CR��A"�z�����E�G���a�Nt(���&�:K/����vZ����N����D��l:���l��l�#\�xs�bM`H��$�q�y�*��9Z��V�w^�
Na6�z��%�q��|{�4w��Q�X��y1u�7�����1qS�#��j�P�(Z	���c``M� U���������1�B��;��G0@������D-���o9����*���f����Nz�N����4
2��E��(m�_�Y���tB�*F��*�	e�>t�6�2_B�_�B��VM�=��B]%x�Ck?�$0�d�����;�Ji��;t�/�y��"����ouy!�S=��z�!�Ekyr�v��ER6
RC���y����	W��%�t}�0:��.r����B������u���(�,"��D
D��jN8{����T5�3������}���H�)����aIF�q�3�d�Diw�N�r'�@����_>�9J�uhkm�X6#�p���*}�6EI���t������@:%R;2����ie�S)i����	g�&������4A�
���[�p�o�1x��o!&�`�OJ S���GL��S"0�������J������m�����7/��R���)������%���{s�����5+��Z�0
�	�!:=�k�W������,rX�K��Jy�!�����Y��t&C�cV]w69��-Zi���lStP'z>g.)v�f��_�%�����W%��2��a�%W�u��u-(
h��v�&S�����\�Qo��)��r��p{^�!�]�u��4G���|e���PZ{�=(\uf<�0��9�-����)
��y�������������e����0����
����Z���F,��@L���4�<`�-�blj�c"�����g�%i������i3
a0���9O#N�9�-yF]����a*#�Y�fp��WL@���	�`��P�3�g�Fk��t!/��@��=*���h)�JN�����?Th��L�a$��
Mg��p�@��������������r�l�]���_��W�O���0,��w=&'��A���>��z?t��a�)P�[\�nl���Y�K����Gi��|���)�P��By����Q0�O�7z~`(�|f��� �y_�2���Z��|�vhb���oi�E�7q�h8����/�k�HD��h�9c��MA>��4JE=�s����K(��|V���K�^�@���Dx��<4g������h��q�3����
C�&k�`h����6P�|U�lZ ��A<��\
�Uq��	�.�T����C�[mS���6~[�zb�h���|�;�'a��	(�����m�q�L^0�����[xL�s�Z���cb=]\ k����E���)5�Z��R%�0Q �	�s��=��j�
Qc����d7�(�W��>������Zs�
�a�=Q�.���/p0\�����������.:�Z���0��S�$-��15�IM��-���������u-%��l���,f�����N����,.�j;�l��2��`���a��h���r��S�]�A��>�Z�4�t��*i|�y�)���+~�,�����8���<V��U���Eg����=�����>�H�,@i��i�+��@��8�����eP�
�@;9@J��1���|u��tV���)��5\t�g��D������vQ������%���D��3#�(����2U3�BxH���9���`=fM}
m-�:�V���:����C�u�l#��]��j�/�������G�{�>{������O�<���'o|����_�q��n>��t���?����'/�����������7�/�~zs�o[��?���������h��%=x���^�����{���{�v���������+���|������~��>���G�O�����>|���N������Go�����g_=;�r��?y���O���^���'�n�~v��W/��_������{���/N/����7.����Z������[�����O��v���?|��]�zz��OO�^=����~}z������������?:�<y���7�����z���/�>��>y���X?{��?�/����{���N�����W^{�����f�@��������u��_\�r�������o����{%��|��Wo����|v��/����k�>�{�����7����_����|����s����~��_]��G��o����{�W�z���><�����~�K��?����~�}�����������k���g_�|����z�{�r:�r�b�_������������s]�n�����=�����?�}@�����o\��;����|���b���{�W���|������#����|��������W��W��/�_Xf��_���%�G���{}����7�Eot���sg���?�|���s�������s?��yj}����������\����?���?���~����Z-��?�����s%������i��k|�u�}K�{�7^X�������W�/*~���R�e�������#J�����
����q��o?�~����x�q}�p�������
#36Teodor Sigaev
teodor@sigaev.ru
In reply to: Alexander Korotkov (#35)
Re: WIP: Access method extendability

Some notices:

1) create-am.3.patch.gz
As I understand, you didn't add schema name to access method. Why? Suppose,
if we implement SQL-like interface for AM screation/dropping then we should
provide a schema qualification for them

2) create-am.3.patch.gz get_object_address_am()
+       switch (list_length(objname))
...
+               case 2:
+                       catalogname = strVal(linitial(objname));
+                       amname = strVal(lthird(objname));
                                         ^^^^^^ seems, it should be lsecond()
3) create-am.3.patch.gz
  Suppose, RM_GENERIC_ID is part of generic-xlog.3.patch.gz

4) Makefile(s)
As I can see, object files are lexicographically ordered

5) gindesc.c -> genericdesc.c in file header

6) generic-xlog.3.patch.gz
I don't like an idea to split START_CRIT_SECTION and END_CRIT_SECTION to
GenericXLogStart and GenericXLogFinish. User's code could throw a error or
allocate memory between them and error will become a panic.

--
Teodor Sigaev E-mail: teodor@sigaev.ru
WWW: http://www.sigaev.ru/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#37Petr Jelinek
petr@2ndquadrant.com
In reply to: Teodor Sigaev (#36)
Re: WIP: Access method extendability

On 2015-09-07 17:41, Teodor Sigaev wrote:

Some notices:

1) create-am.3.patch.gz
As I understand, you didn't add schema name to access method. Why?
Suppose, if we implement SQL-like interface for AM screation/dropping
then we should provide a schema qualification for them

Why would access methods need to be schema qualified?

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#38Teodor Sigaev
teodor@sigaev.ru
In reply to: Petr Jelinek (#37)
Re: WIP: Access method extendability

Petr Jelinek wrote:

On 2015-09-07 17:41, Teodor Sigaev wrote:

Some notices:

1) create-am.3.patch.gz
As I understand, you didn't add schema name to access method. Why?
Suppose, if we implement SQL-like interface for AM screation/dropping
then we should provide a schema qualification for them

Why would access methods need to be schema qualified?

Opfamily and opclass could be schema-qualified.

--
Teodor Sigaev E-mail: teodor@sigaev.ru
WWW: http://www.sigaev.ru/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#39Robert Haas
robertmhaas@gmail.com
In reply to: Teodor Sigaev (#38)
Re: WIP: Access method extendability

On Mon, Sep 7, 2015 at 3:32 PM, Teodor Sigaev <teodor@sigaev.ru> wrote:

Petr Jelinek wrote:

On 2015-09-07 17:41, Teodor Sigaev wrote:

Some notices:

1) create-am.3.patch.gz
As I understand, you didn't add schema name to access method. Why?
Suppose, if we implement SQL-like interface for AM screation/dropping
then we should provide a schema qualification for them

Why would access methods need to be schema qualified?

Opfamily and opclass could be schema-qualified.

So what?

Making access methods schema-qualified seems like a completely
separate project, and an unnecessary one.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#40Alexander Korotkov
aekorotkov@gmail.com
In reply to: Teodor Sigaev (#36)
3 attachment(s)
Re: WIP: Access method extendability

Hi!

Thank you for review!

On Mon, Sep 7, 2015 at 6:41 PM, Teodor Sigaev <teodor@sigaev.ru> wrote:

Some notices:

1) create-am.3.patch.gz
As I understand, you didn't add schema name to access method. Why?
Suppose, if we implement SQL-like interface for AM screation/dropping then
we should provide a schema qualification for them

Per subsequent discussion it's unclear why we need to make access methods
schema qualified.

2) create-am.3.patch.gz get_object_address_am()
+       switch (list_length(objname))
...
+               case 2:
+                       catalogname = strVal(linitial(objname));
+                       amname = strVal(lthird(objname));
^^^^^^ seems, it should be
lsecond()

Fixed.

3) create-am.3.patch.gz
Suppose, RM_GENERIC_ID is part of generic-xlog.3.patch.gz

Fixed.

4) Makefile(s)

As I can see, object files are lexicographically ordered

Fixed.

5) gindesc.c -> genericdesc.c in file header

Fixed.

6) generic-xlog.3.patch.gz
I don't like an idea to split START_CRIT_SECTION and END_CRIT_SECTION to
GenericXLogStart and GenericXLogFinish. User's code could throw a error or
allocate memory between them and error will become a panic.

Fixed. Now, critical section is only inside GenericXLogFinish.
GenericXLogRegister returns pointer to the page copies which could be
modified. GenericXLogFinish swaps the data between page copy and page
itself. That allow us to avoid critical section in used code.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

Attachments:

generic-xlog.4.patch.gzapplication/x-gzip; name=generic-xlog.4.patch.gzDownload
create-am.4.patch.gzapplication/x-gzip; name=create-am.4.patch.gzDownload
bloom-contrib.4.patch.gzapplication/x-gzip; name=bloom-contrib.4.patch.gzDownload
�bH7V��k{�����e�	�@���%�����m�������iM�GF7$q����������F�d�����#]�YY���+VD������{;_e���g�������~?{3?�e��|t�l�c��.�g��L���3?;����>�+������k���{wC�w����������w_>~�� {��{l��uE���_����{w�lkkz|:?;�}�����������d���`q8���d:?�}�?z}�X�nq���b6;|/�^���������2��i�bk������-.�c�~��1��~u|��x�fm��42�+��F3�6����2z�f��}=;�]LW��ly��x~1;Z-.���������g���lyy�Z��W���G�fG�v�Xl�������TU?����M�o�kdi���c��~�i���������{��z��Kc������u�<Z,W�����rva�.��g�����d�aztyy���x���_��X��|������g��������������?���_|��������������h�\f�����qv/[z��.���O�~j��~d|������q���7��7�w��/���A|������z��������7����?d�?���|7;9�?��;~<����/6?;:���.�1���d���7:ra�8?|}9?9����v�����
;�-/��<�{�x==�r��'��G����~�����3/�7I�������_�3��)�'�����RL�d�K�Jp�$x����b���x��A����yU��X~���-�����t�C�����2�y��rg7{~2{�}9�X]�-���?-�������K�o-��;��4����~��X��_�����z{:���?Z����_��/�g���r9{sy��2[M_����z���������+�n��������2��4�A����M/^�W���~lN_��0�g�������<�>=��z����r���z7=���P����?3��>9�.f��J�������}X-�l��/.��uq�X.����)�����.L��l����L,�N������}9x0-.�� �����W�������������Y����9����CYx��,������A�/�-.O���3�J�c�?�'��M��1���|?Y�7�M/��e���7����q��o�����t/f��OV����/��i�������{�����N}�q.��^��3���������>����q���|v<��_\��,��Y���n
b������,����~I�g��{^���~S�V���� �6H����`��Yz����p�����=��nx��-q4�q[���}9{���M�}#q�B�q���D����h����C'����w���,����\�����?%����C�e;;�<}�[��b:_�`�>�v}G�����w�l����3������3�#��m�����+�/�0=��G��
z��=y�x��&����2�G���s�;�w��N�/�?<y�M�mo���
�r��3�g���y`O�fv���Yv��o5Z��s?W��������IP��Kv���x��&,��a��~^���_)������z����W���^���-��N�����t��6�����yX�Z�Ah..�������M�������VQ�������
|h�r9}�X�N�Ju�-���2j�m��������?%;;�>q�~1��_,~��_����~���_l~��_����~�M~�7���_,<����_��^��@-�\J��c����6�����'/~��l�q�#�"���_�=���M{�]������m��|�s���!u��y���Dj����?d��^yLO���L
���\�Wg~|7�����2n��U�v�������_�K����3��{���_�W^��}��� �d�\g���z��W�!��������W��a��w�yy���Q����N�O������n���~q���c�����������l{�o�����={�w`7��j���}���$�������'��~�{8����������:h��i��W��o���,��y�[n]v���*>S�`��e�����
��I����}�`m{O7�j������<�rG\Wn�;J��Y\C���]#�6�y��*���������h����vi���l������a��u�O����|M�2����  �����/ ���?~U��J�
����`Bn�=\^WU�l�=R�6���f%������$����RO�_[[���\�����������:.�h��G������V^
v�D9������������Be�g?�N��-���X\�Zx�x���'_=y�����g��,�f���~����?V�������0�������}��8�#�-���b'��Ft�3��xnj2�a�����8|�-�������74�x?;�{�X�vm�|1�^��s�_�F/.����/�`�nm��������w�
��y�dw�y\K��������? �/������G�Y}ai���������������{���W���z���w�>[�#h??
�Zn��Xb?n}v7{����#����+j���������fv����>�E(���hu���z$�t�����l��q|�n6����x��rgg��?����e?���U����n��q��n���]���yew<�/����1h���,{kC��Jk��G2bba�0��^~7�J��"�\����=I����������[�/n�����p�_nV��'6)u]�E�w��U�cS�F�mT����q���[�����C�5F@m�MI�����QG�;��E�>��kz����ka��t�x�~���/�����^�x:��D���_X�����^_n�\t5��fy�[����N-
���_?��;/�>$����O�%V��g!df*S�=J�W�Y1/�/�������[�~Zm�N�����u�nz���c?>����G�;��?���g����������/�x���-��H�������x���o��.�_Js�Q�]o$��=v4�N/��n�����XC[����b�f?���nR��K|�x���rl�p��Y���F�������n��vt�~;|g7��x��0R�5�P�<X{�W������}������{?��rFo�A;�.7���4<����zz���E���w~��OW�����'��%&�����������������=Y����������l
&������;����w�����������f������z��|u���b{8�&�i�C+��������o �~g
c�[8V�i����o�
/$o����jvzM�WE!tg'{�0{���8�[q0���&p �����o������/�<{����w/^l���4.��s����(�p?9����l����W��:I����ck�oCo��{=����E�����G��h���n�
�_{	���o0��z�"��_y�O?}�[�He-����r�r�>����h�I}pqW|}��/�z���Wn��9
s�:6��
���b|��f���������dc2��[A�VS��AR�|�]����=[=����o��l�:�~���fv4�f���G�����������HT��PP�'��g���
y�QC���1���EMnR���M[����F�����pH�[9�g[��f��{���?�WL���>\���
o��;Lx�[����y�	1A��G��z?[�V@�>���Y�f�z%p�-��R�G���=}���������_�}�����O�<���[�{��������?����^Q��7�������V|~E'�U�?��j����m�u��	����n������;������t�-����M-�f����h(�vY�=a����v�
2����~�-`���E��4z�4�t��������./��^������er�~�F����F���	������@��l�5��y�W��������8��vy��o0zW�x�3���7w6����Q�����}0���gG�����x���]<4��������oT��S�f��������t�5�|tdF7�HV���Z��-���c:?\�7?�f����u���x��b��HXH�>n�r>l���������%������=�ZoF�&���w���^��7�/�>�������^�}�����={n~�'����_�����_������7{/�{E����:,���}���B��u<�����/�����6� ���u!f�#��/-V_��������?�o�-���~�I���>`@�{~�~���>>�j��7����GO�;x�{��F��!(�9^�ys0�8��f;�M�����m���r�kv��b���?N
f6�.�v:��z��������/�]���4����WW �gN�1}���U#6����Yh��-���F������<�@�|���`�Q�]����r�+n�������bz<k�������E�7>�x���]�Z��	��i>~����W�ur����������%�&��.�[���O�o�������q�KV����.���dp�����/wkm=�����E3�����������/��3�a0I��������}��W�6\�8�������MIR"����D8����7G�k"����m�r�n�h�����^��x��h?���g�C�>�[�����;^�������`���\����OT��{����o�?�?�{��w������������{�($�yk����!�|�������}7�V�x��wx��r���GO�BGW���<&�`�g_������~��y���k��w���r[��=����c�����M����x��gs7��]��p��nDW)����B�'yo�����5������������lj�ZyMw�*�U�/i��_wv�n��D���~�}�s�����r���biD�O�2
W�{_��{�lz�'��d��������SS�{��������xw���_��g���!w�`��}�]�����9A�Cs#�9���g��gw6�������/�sK������[g~�b8�R������������7�/^>�
w��3��KW����w{O^�U����l�:����&����Fx~>��e{�7�����O0��,;k+����/���f���������Fo9����o������O�_�?����<�c`�X��n��������q�A�J�����;�4���'!�z���}a����'�h���D�����\��Y�?�Erc����l��~X��g?������������W��l�ivw�;�v�Z�<�����E���,�RXk{{c�v����5��&�."�����R
{5�sm���mg�����7����6w����������#6,�o����Z��^�<?�l�LZ�w���~89<��>x���{,�!�z�x��������*4�������>a��1O��V��|u����bg�S��/h,:#�e]a���������L�3��7������?���m�l��~�{� �u��o�[E��_V#�hmP�;;���(1:�"���7����M��!�r/��|��`8D;;<|'�lp=av�O���a5��H�	L��GP�f�8��S�&�(���/����i��^�@��������}����/�����{�&�M������=�/kSg��kw��S�W�h�����W?;7�E��i2��VJ�!&�Wg���`����3��E�h��c��M���,+>�H�Y�|�`����~���=Z�\��b~x��(U(��%I���]#�3��v����1���+���y��^�-�N�?�A����v�iz��O�F��f+i��7�m�n�4�������Q�����;�z��!���C�B�����}�z�������h%],���������Q�1��//~������5'��������9Z_���{�t���/�u�vT��}7�b�����/����[�����?��K�S�=w�f�����z����|������3���,�������_u�>���l'�o�Us��C}[;�_dso�M~���7=}��i���}
��}���������AS���f^�s��z���{����
�_/
kf"y
�Tt���HY��	�H���W]
n�)��H�i��w��X|���eS������A��������=5g�z`�����c�^~}1����P�$�~~�(�VF����������.�m�����U�F��|����X����,��o~^��oyJ��������Kb�@,��P\(�AtE}H���z,}C�CR��|uq#�d�����}��6t�	��+v�����4������r����}���&"�`$�}a�<�-�k���2p�R�6Wg���/�uq�7���G�S
|og+������^=yL
�����
+�b�����������|g����t�zyC�yh0v6?u�������������������>���w��������}��qw�G'��0�����r���>���S�,b���vXU����E�����M�R�$��:h5��Nf����_����x��MC�X���^�&q\��6���M5M��'�j��u���omL<�%�t�S����p����7%
������f2��]kb3B���!��o��MW�&F���5)p��;��)�^�b�� ���a=���~'��Zi~#=������f��6����2� �{�}��:�����p5�N���w��{��8��T����v����_������bh�G��22��}����)L�7q;		��=��y�<h����g@��B].�}��h����Pqnm
_�?��
<7{�N����o.f�������G���h�l���?�_gp��<-S�=������������8fVh���
����	�`���A�n0�����)���4n�l��7�z��(��|�_�v�������h���w����<)Z��Q?�>��Gb����w<��l���t�z�����8�l�w\���Vt8�Sr�
5����z=$�
����{�<H[|�r�z�{����<�������$��T�\G5����a6ZN��7@S:��uD,�+���#9��hvXC�?o��81�<���w�����8����f{���i��������+r!�����0M/���U�����5J�B�gj�u�.�������0�7��P����>9H
<un������l��ln��Kz�����������0�!���m�`�H����,u}z��2X^��n��tO�6b2Mx���g7��8���0��w��"���gHA������d���V�x�?|J�^.g����bq~1�=�%���g�,��wz�����[\�����{�_~��w�~�
�-�����Yu��� ���t���z�E5����	�����6�$���Ry���xv������{O�����=����?��p�����/��{��F�d��$]�!:N�X�W������e;$�D������:au��e1N�B_qh)����-}(����������E4��k��9��%l��sa�n�������%��P��Y���.�^��b�vq.����@���������+f�N�c/�(�[#U$]t:]E�I��_���t�&���+=���������m��n����h��wl���gK�������[���7��B�nM�
:������z�\������I�N����������^>���?���	��K��7��	1�o�7��%���5��e�����\�h�bO�}����n;�� �I�I(�x~������w�r�_�L)U#����b��]XP<2#�A2"��k�ww�@J�����~
;���g^}l�oJJ+�{���)6�<�x��_�	�x�����v��w�~�8���0?N/�o��2�zn����d5?Z�v��=&b�sb6�cP�Y�����Z�p�	�
]>�Y5������bv-��o��������d-?L��M�`��!�t�F�m��z�������p��_����x��z�P�x�_�	L#����=.��abE���)]sC5�w�?7���������"<���'�t�4#4�@&}p����Y����nZ@���8��,	����hOm��nD�nfH��*���yJ^M�����������������f��21��|~���������kq7�@���w��7���j�d�����9x��e��_���/�_5�.fAk�l�d��]\��?~�����y~�������_���G1�?��{e��~�wN�o�?"�3�b<=1[>���rX�`���jy��%<�������|������W4�����	7����9�%*NNF3�|~�d�������?���&v`�o7�=N�I'������=�hgg,/��cq~���O��;����B3����������#����`�
V\������C�J���-D�61�������7�s�T�����n��-��^��4�=�i��m�������v6�(1����;=Vn�v��
t"�����w��W��5������������Fdh��h��$��D/8&��	qx�������'��������Qo��1�Qn�<�4`
k	X�on�s��B|������^���f�c���p
��n�nm�"�W+�l��<��k���BO�
4/D��z=�X9g��s��($n�
�	�������w>����z�<�ka�>
kS��~�,9Qn@5����Y��y�V�&�yb}D@�6�����o�e��a�;�Y?��8�|j�%��a]�������4�`��+��������^)g=��?}
5���<�l���o���fg�D[z������YK�	z���0���/�~��G�i�3���q��W����(j�O��
�/� _�(�>;�([����
Pd���P��x���q��p��9���r����1���#�;�J7�1���u�2�|��3gGo���v����.d��&$6���b��4$:�s_8/6��'���hz�T�i����l�z��l���Kc���^�s��]`����R|�?Q=�=�B��Y���q[���#F�LJ���o�������b�kg�l��>T(x���c�}�b	�X��W��t����CD+O�&2(!���X�����+���M���GZ�c�5�������Fe�^�F���p-�K��j��m]c���[�fO���~~��D�}������{~6��{%���N�����/{�46o������u��S���g?�O����w���Meo�s��hN��W,�>w+M����^��0�1��������n�6���+[��)���5��W��b!��<�E���+2�n������e ����}~|x��1�y��Q�������_b{�O����O��������tfF�����z�n���d{6��m��y�G��N���=��B�1��N�����5����8��`�����b=����G?d�����^0�f����s�W��;�R<89��T�U��]�=
���YJ�1���������;�J�vr�������	��@$�
�}������(�W����C�g�[-~�J���Z�,�D��m���|��<���
��a��ltt������
etW6��X�x�Q����Y8�:0 5�j��(�x/Q<c#��t�>&&	�u\.���^���9����4fWD��"n�����2�}�~��,f��.�e@�����l ��x��of����a[�x����=���4����q�.��I��p�t8*��F�z�����2��TB�����H�������=�=���=�v#��mD���W���U[H��/���I�A�_>����Z������ ������z|����F@��gc8#
�o��t�q=�-��������n7������M1�O��Q	f}%�����f�y�>�RQ�kk�u��G��|8��������87<�{���M��^{�U�fh���
�L����������@���/f�\�M9�[����f���
��FR? �u$�WQ���v�$���rg���-]�N�|E>���7��<m�������N"GR4|�:~�a����_��b1j����Q����}�<BU�/�>�V�b+���W�,UT���:x=*0��%~���|k�������G'Qp?��!��HLA�E�Clc�"m.���[k�l�X.��/|�o���[�w#������O��o�y��G��i�J0��B�]�g��j<�Q���>)�+��w>�����/j����F?q�w���t��B�g�'H��������d����^+�\�?���QfB�?R'%�4��Wa{xK���W��	��M���]<O#4$��<e�I@X��������Z�W�{�z������y����nx�����l��c��'=�9��ZZj(L���l(���b��`����2�qkKl�B�_�3������;�${�������e��������2�w�N��
���6�s�_$��b�����K��b�v�T���TN�����M�o��m����_y.�����\^��z'd���P�f���4�>�^�����Q9��G���8z5������_�mK�7��=�>��s��@�Y�	�`��)��W��6'}HQ�>C,&��0o|��4:���3�C����Eb6R�Q�����tg���x���_�����	����2 �j9�,�,����e���Y���}��f�v�.����+~��1��D
3�> �g�|��i=kY$��#&1���Lw�*e��:�ry�u�����������i=�C>n�����
�����y�.}������}��vG��g~���������3�K�~�6���,�p���=\	5�����g��	�#����w��7�R��Z�!��y���7s�����6��w�;�����Sb0�A��C;2�K�%�}Fp�������y���Zf��Y �����x.
�����~���Jg��$���)q��������epm���;�2go������6�d_6)h�O���6������>����q���`�������2�h4Ru�0g����p����������&~��F����(H�uk,i}r�%k�����������aS��h�������c{�	>qT?�����g��%���!�p��������_\#��fz����O������O?�S�����������0��3N���%��M8lW��_{������� ��@�Pve��M5kd��9.'W>��0M���/��d`��5������Z7:#W���%�!�#��>�7V���^���$[��������.�N��X����.Ao�o��?����M`��*��cN��$T�����.W���/7������_)u/w��oru�_x���<l�s�����/I����M,�M�~���rv��8���L6��Q��+��5�Q0Do)�YyQ�V������y��1�p�P�������	x���(��!�5$(����k��E�YK�����N���,]���M��� t}���������B��V��M�����V~E<��+��6��������&��N�BI���[��Y�y�zS���&^-�6~Z���C����C��^M�x=�r{jk��U����&�syk������vb�+���S����������|k�*6W�^��u���M�-��Y{�,�b�{�q4r����������8�O?���ug�T��4�����ap6��r���]����o��'�]L��v2a��g����mv�$>6w�u[
W������Te���{��^�*lB\��;{����/*�����~$�����6����L���p����y������#����U������*�w�E���^���u�����U�\��uwbV�6��[N{q���z������,�:���6���}{;7����I�.v�����E��2�g���6*�31*�Jbg��9�b�+'���ZR�J�F���k5N^�2@����x_��j�om����L����M&��9�o+�2oL����0�e��d�?.*����T�����c�f�WM��^��4z%�l?/�7����U����W�j�����m��:Y����8#6�ye����C�
V����������H{]c�o�@?�q>j��?���V[���s��t�l*����������k�������J (�8^��g��nT���������y���OB�j��Tj{N[5<���F���f~��k'�o���1im>��u�UQ.Jk��!�lft��9��7&}ui�X����
)��#�~-�����i��^����Z�`6�d�d8�`�����)�����
vY���e�����r%���J(mgt��hug;R�M�"��L���#Hj�8��Ui�l����f�*Qq��FW�l����
�hw�=S�AZ��\�_u���`��-��g�0Ry�
����	�o=>�d^J���-�Y�T�SV,��zQ [�T31MW��h*(/�K���1p-��}�@�a$���6[1I,+�p�.<�`��:i[�a��������0������"�V�b�t�nE�����2�����IB���
kSmR !�Vm����Z�
N��fE`�6���y������oj��~���t����{�r�
,�{�bS�Z
SN����l�[�����$�oRs�<�d	�L��b_>g��b{]�d�z�i���6��A���bb�m�Kmr�[LL�W,�}a�H�� ]�9{�����h�x�	�����G���$��(����+��5m���&�R�
�C`s3��	�>�K��d���@Fe�q�
�
�b�-iC��������I�P�����b��LE+%��k`�u�L����^���^���l��m����N��~�,j�{n��St�F��i�I|���s-����
���m;g��OZ�>�3��C��D�+Pb�l
S���0W����uY�cv�\�8�E�����X��lnv:��Zw2}s������&K�dZ�Ar�em�JQ8&
�h1qk��O[�
���Eo�j�A��8&�e����(Ctu]V�c�J��d�,'t�K;63���'J�D9M���� ���%�Di�S+]�Cv�8U��<A{�j"�U�����>!������k�7h��%���r,������L�Y�-
��Fk��ED�[0n%�~����U��[�V�J�
��l{���(�|�3+�1|:P��f|��/6�-3S���s|�+�%��K�Q�������m�i�c�0�,j���&�-�Q�E�v��T]��wo������:l*)n����	���`3j���U��5�t��HP@L3o����m��c\�A94`��R�����/1�5��k�n����X��dL~Tv�����)��s��6�W�K����s
Z���C#�J4%N'�S�����1]���d$.>�6���h�Vs���4
����Z<K\C���5�0��(J@ 4h
��1�c������e�u6�������k �/���'�|�^�e��z��-�SU���������. 	�kn���s�+�������:��7�qB������m ��|`l��=��1����S���VF��YL��%l�` $��a��8�X��!������Vj�q	�Q��m��0��]����ikd[�H��H��k���Ah�Y6-"S����$��Q���J
W��*�8)�O�X�����I^j��X�fz4X�U���q�O��`���tS'm�+�	�m����NZ���8[z��g�1���7����%��\f{�M���O�\�jUcD����e"��C��HV�R�r|u:t�3O��U��
�D'HYFF�)�iv:���H[J�ZX���/��e�
��P�*\���H&�-��z�c�
^�1�0�\���*�g��d_���9|�	F�]�[���.�%Z�6	���VH^��#�B���>o[T,����Q��JL�bP�
\<�o��g�Y
sn�z� �q�4�2�m���&���*�5J��]���B���5����+/Ek��'��t��-+=>�QUZD(N~��J�W���Ljl��,j����!���d�@���AU���)+4�IQe�"Lm���BG3rT���'�`�Cw�V�(�g�7k/�B|�
l�������
�p�J
�d����NU �"
KXv�����z��g���W�
�������Aw��CV6QZ4@�]g����n�+HJU%�6��-��b3��N'�0��U���P"a0<�IK?*�N@6+���v����8\c��
P4@AB��rg��bn���0`Pq��yJk�(58dE�M^����3���9)�Z��G0M��bL�^�����)�h��r9��u��1����E��\bo!��PHu2r-K�I��M��O{]��l��u'T�<���B�jS}�����J6w�3�gs;Wl��g��@j�+�*Z���y9Y^J��Kf��b�Y�������o
6'C��l��
{��~GQ�n���`{�_]*tX�=�*�:���l;;m�#;,��-@�j��D�@J��"E	T����k$9��2{�����Y�r�H�tX�9�T�v�	S�����Z�U�;�p4���k�Q���Y��
MS��i�#
�|�`	�`�,�V[�������OHM��w�x}[��a+6!g�,���5iA6��9n.t��!�����)��jk�#�����8��c�+�hQ�����6E>rb����K��};��������j�/yE���U�-���u,/ 0��T�?P���B��>�
��)0`s�m`��3@<dB�f�:�&@4�����N[��9��0	@c����&�L����u������h=��qgL2l��x:-��p/!���5��`F,�Z3��-�0��
��*���j1���j@����N�K�	bF��-+��&��B
s`;aQ&T8�����';X��	k�5��5��rV�aN������|���z�=	`dx�(J�[�����c<���f�,j�FiD(��si6X|�L��U*��M\�Aq@�%J !7�H�����;�8�hv�}d��d)����qVr\������X6P���h��KBrN��T+{��&�����y��`���
�aG �$2��a��)��%������	A�6�I�	j����c�/���/X9�DaV���*|��������X&�R(�p@gRT�W������H6��Ja��
���aD{,�d��W�h��Sf�+�XT
�@�Q����4L�4	!7�fo��#�.e+���pb�s�m���dV������.���R��x��~��������{:�D�eg���+��<C�0�c\u�t(x^��o�
�9.��M!a��n��S�#��c0�P+�L�\��<�v�D�TM���!7���M���?�	���=���$c�7������+0�uW��*E��d�r��p<����@��
�-V[�\	BDx�E#s���:�?�`'��N�t���F[���r"�x�q��-�NI_+��*:�G���a���b[�'[(����%9� <�NtI0 �"����Z�@���(F����D�[��"!���A�a�@	a�a��$�^�r���"��j��k8�0��Ep��U��Yy�R����nD3!�V��
L�'hf�����:�3X�5���T$���+<�@$0��,�A@���������bq�X���8f������
g�K�jl;������X8�
�(��r�) ��LU�8��R5m��H�n���n��.9l'Y���8V�!FD���a���'�������t�DH3��DMr��o�l��[Bz,[�F����x��@f�5�@��
&����=�o6)��T#h�1�Rk6�`"
x��v,e3�-D�� h�3V<6pC
<^�u��NA��0`1C��6S&���2E`��j�@��J*�bN���d*���!�!�g�/B�Q�����D�8�6O�������}��_�*�
���Ym��t�Dxo�I�zt�����S���Nh���tN�D�%\
����TTK����,���%Z:+(��)z��l��fj�@��Z��5V.\"l�xpL���;�X�b�y�����W8�������	F�mu
s�a"Y3���2�o�&���8!�&9�z�J�s9�D)��8��L�T�_1
aQQ�L^���/p�\�D���`"�������S�!Tw���~`MD��+(��iqi@z[�b��P���M0V�r@��N;Z-����qIC��Q�S��v������(�����������L?�$m�tpb!�n��YJH��+���@B����e�k�!�V!x�rU�J/��m�"!�{H��rSCW`��L��pg`�I�#�%*K�<ki�*G��`����
h�%�����S��[�19���C��r6$	��BT:��	iZ�(X�V����_;gPr`�KD[z�d�L�����k��*��D��(�`US3boA��?� l0/����r��G_L�M%�Q�=6� ����I���$��	����$$Q�@�qY��9�s��F��YL���G	kP�[yN�p�5y
V���������$v���(,��-,)�dK�d��jM���A6G]

��+k��LB��'0=uNJ�h]^L�&o����YP��I\K�b�Jb���)P�Q~f!.���(�DR�-�P�<,�D��1G���y
�����'�$�b�'u�$��3s!���(%���Z��������"A(��D���P��w��P�^j��l��-+�$
���3�,$�N�d�D�Ms6���D�3��0Ej4��H
�PU*��&)�N������ R��:,$:�&���06vP`(��S�2�DQt�T][�
>�N�?%\k�9#W� ��f
"l�>����������k���p$�������|m��R�y�"��u{��5g���B����p@�W�+������_�f#�Z
�,��ED��
�WW�������:�L;�y-.e�:�=�:K�6�B"6�V��������7*�����t��p���jAz��!���=��% QR�X"���Csq	s���zjp�
c�bv*%t��b<#86���m'	2�D��hV�N���1d���[K�����S2"uan��~.��&��$b&��X�U��YL\�7	o
!u0yv`��4N�|R��?��S*s�'X)��P��}H��"���c���<"e��D:r�m�@r�]��NX)
x��yC_��������
o���#�WpF�E���&w�4��<�	u����_!�����
@'GH��J^BA�O���PL�D����h�x�e���6
�_';"��D�J���z)����He6z��a��L{[����O��S�}�U}��pvqJ���v!+�lq����������Xh0����%+qE��l�2����=�)!��\YU�h�qU|�����%��R�<������d�_����Ry�+P�������:'_����k�oa��	"��\e��J�!m��W�����!�/�h�c�"
����R=��T
�����C�`��(IB���e\�N|!^�����
�r�s��$���U�4D���"�p������+,���u)o��b����`t������F��b
!�Yz��-E��I��m8���E��h��*��)���v)�-�4Q��J�Qb�p�jb�]%�M��
�
-NVU�+h��.��t	z��J���D�p���P�,|uL����w �\Yn���,�.�I(-p����uI�b������d������y�J	�����#�L�![�=A/�L'�L|5
���`�+�B%���Cui�qh�	�(S�@�z�(�l�u�I��^sv�D��v��hH�-)2�m@��94��D<��]�l��V��u���:"#�P~���@�P.n�"NW�Yt��&��6�e��,���D���"Y�qu��@�����N)l{��T$�$%�)
.XQ&��`C
N"v���B� T���������F�c�'�j��Y�@2s����������EM��e�������^�� �Q&`d�BB�AP����C�d����XkQTP�Q�@����������E����uv<xL��!�����
x^����dgI�E�j8P�H/}����p8.�RY)Dc[��]"(�A��:�����-� �x�Zap��KL����>�~�o��S�%:�A���@�Il([�CX��%����9������L��������
�_��/m�!�,�A���=`Z�!��Z��Rb�@�m@�U����]%^vp��+�E-"=�r`��k�jU�"��I�*s�����2!�FErX��%lAq��DaI0���x�l�
D�<W
���jj���M�BM���Ju@\`j�;Gvh ���@-<����b�����e���N���8�r��k�@`E'�$h�|�������vjUgB�j���c�1X��m����V��|�Pk�*�xcNA���M���-K����AN�G�&����\�)1�����"����R�
NmK�P�`�w|�b������{�%�?�r�8U�+��"r<�K@E�I�<f���Z�Bk�GT��	lD��R����e����y�U�r�a�U�i��5�*v��������%G���U6	�R�)���D�Va�2e��a6I��h1�@����RF�%\�,��VqJ�����LhT42��P����R��o%��$e7�;��kY�K@���H2E�m4W�@��������=x��Q��srB���q��(�C#�K�k�YE!��*$*�dD�J(V���kWCl�[����"}�������;-L��$���&b�Z��^r�kp~G,�&�\��%VT�
')�o�y����N5��J
W�D�.DkiH���JR�G��DU�x�����V�i�J
�(N�b
U���F��jC�`�l�����U�4�a�T
�u)��K(l��p|��c;.T�������dQ�+�U����G��9)�	_%j�K�/V��G��J���P�m/K��T=G���L �p���UCa'>��F�<��PY�BYiT,$������Q�#��qn�r���I�(kE�'i��(�R��6Y�5�YS(2���GH�
���I9��a��p� �fb�B��9�����z;�%��zn���B
���S�AY(I��P�����0��Z+���]�0a���w*3��������a�9%U2�S5��S����e_:��!��%J�D�$���%pq����4�
f�"02r��e��<���A�i%�`�y�����s��*�[Q�Q~S��W�xh}J���J����9��z^�p��D~DF�+��0������J��hd��'�P�Z�()D4n��d�B$T��{:�	S���Q)5����R�m+w�J�"�m��5�MT��f��*I	);<O!^������� M��df7e������/�[U��[��E��Pj��z%�Y
_8M�c�y*��\9�h�Z��g,���Q�����K+�&����u��M�!�p�8�iR����:%���aS;xy{�T�������1��U�RW	�ySA��"�zvDF���jg�"m*��{�N�S�!�T�6����a!�"y=
�lGKY[�5�NtP��B�jT��F�Qy\v�
�9��e��Zx���S���T�as �����B����&m�m�G�� ��b�J�Fo���.�����yxpa�Z��WHO��S0����
z�&����]�@oE��iS���Vyv�Z��T��/w��N���+�|��>/���
r4��X_JC�h�S�o����
�=�bn���+Q�U�[����x�4I|��E'j:�X�q���m�2e��=���'?D�g������Q��& �<,r�(�:���8Q��08��c����Wb
�#��X(�2 ��$���(V��������as
���c/U$�C�+P�W����#_k��reS����#�I��7a�
?���*���]�L���p�R.F��+_���N����S�2��ZE�qO�`���=
����W��vTl	
�4�"�1U��)��*,AY�=^����+"��.eG�_v����i:�%�V�&{KQf������s��u�=j�C��8��oMT�GP��YA��o��r�+@kq}ZK����3��_���na5�)Z���K���[�l(���� 8�!dIn&�����X��@�7��Q��N��S�m#k���������\B���)�`E=�F�&��\��<_�O��j��M�R��o��S��H j��b��y��N��������Zq3f1Q���Lt} @(X��[L���fv3����
Q����#��z(Bz*�RK8��
X�*<�(���M�B��(��y�81(�F��FYS{�<�
��Y[U�Sl��0`V�*��:��R^ �/���{�E����JA%����T/Tg���Z0��A�!xQ�&�O"_�Pi@�)Z�����?�a��F��@�����*�6�vT��+��\�A��M�VlM�6J*z#vE�b��s��~q��F1��Q~��I�rh��3!pW��H�N�gY�R}�Vp�Yy�����*��s����h��*SS�����J��u����\Y`e%^�h�,W��u|���Z��RB�����D��]�=5X���m��I*��je��B$��V�x���Oy2��b�c�����-����(6I�kiC}�*1����j���,�IyY@�#O���l0F�������`=`c"��B�����Ud��QSS>C�zb���������I���:M�@a����%hB�2*R��K�cu-`�\�(��{W�[b�B/k�9	�nr����-��iE��l��U��)���5%�0Q:�#)�%,�����(a?�Y���S#��I�1$|V���4RCk��+.p��H�x�iJ)"�OU����C�8�t�T2JT��#��
��H�]�$vZ���I�(Q�b.Q�H��T�>�h%��N�(i+#E�E�))E<#��!�:OR��X��������1�J`����R�T�g��S	\�M�X+1�)����X�h��S^��$��(T��*�BA�#T���V��R|�Z�#�'5)�
e�Z|��Q:�
�a��j�#Ysl����Ztc����R
��l4���ik�U�F'����r��T���A�S4JSP�a����Z'������k�s3B��ZIW:�����I�XNQ;�
o�Lk��D�U�?�"�wR�=2hA9l.��N1V����^����3�p�J�59��� "�	$aS���&��K{��������V��7����=j���SR��I)� ��Ct���L��	=��Z�6�$h��74��*E�QE,�U]f���]�N����)���']�	2�}kJQ�U�:q�1@(.��9�����%���l�V�*j/�T�VGf��1��:VU���t"�����R����HP���d�����q�]�	?�V��Dl�M+:�NE�V��
��)PKXZ��������}�RT)����R�Sr|�����c`U��Ws������f�+F
�7Q��9P�a����k��WAXQ� ��F(=� �����Ngw"���Ao;�����S��],'�PrHA
�j�*�K�L��ux�rn)�KNd�������=\�K����T�T�1�9(���������9�:<�����A���Y)^�)�s�t���#�q����o���t$��Q	C�f����*��'���(qp�ZL��v��L��_*���%��S$�S��������"��o��xZ[��������c��:g������[+����pk�e'&���)+�TxU��v�=��;s�kU��%��f	;�BC�"�
�K%�DX�������@�B��E�UQi��:��w\P����:�D�h�#ex�'`�S�M�E(J%W�,]���������jk�1c�QR����H�yU��V��>�� J�H*4�FT�����U(ut�n�"+��������|��<TJ��3�e���*��5��WW:V�����\:������2�S��R�Y<8�3���RA���<��3�r%�R?����Ub����
��a�)�@�������5���zg�g�t
������9*���Y!dZJ;�G^����:uhs)���_IY���^:d�i�S�,^����l����ER��R���)�8B:$@g������b�t*|8QZ;���K����$=P��_Qc^�oa��&�Ft0��{��7�6�(Ba�
Ff@>J���P����i����uf������U�M�>�����X�����_�<M����wbU3���5�2a��Y���������O���W ��O���ILT���?8XM���20�m��U�Og���&%`�-��8�;�����
7���N(q,�����R9��m�^,�&)�?��`��:���n(��P�{C�q��sjL�AE@����,�B�1��>��.U�U���8�� ��'t~4'>TYX6y	9I���V~T����
����IZLi��CcS���
U�������DO�I��m
k���_XC^��+�Q�lQ��5J�eH���^���\�TX�z2�E��^PDNG����(g9OAc�R9*C���H*(�:��yP`����K,�����B��������VUU������/K�>�������u��*�Ra):\�
6��i��5�%U<����Ov�B����`&RG@`���&/a�����#�dLT�\���N�`��8��������t���r����paQ~AY���?{�
r��x�����ho|���&* ���;X����M�F��wRbe�+�l�a����S�+� 5�������1Ii���*�PI�1,'@d���|�TP�N������~U	�����.����%��������Y�����U��I[����'��cK�9R�Q�*�:�8U=o�2�
��r�V��8�* ����v�`�V�n��wV���EG�p[�[��gy^���U�
�&��j�FAMhK}������Ry�s��v)=���5�:�8�����C1��^�|Og^�8�t�����1M��G��	)�mKU�N����D<��'��E�e��Q����j��E�js+���9:&Fn3�9���M�
T�(N%{�/@1)�
-�����&Q����)�'R�:���J%�&��4�E����+�h��x|�TG>�g�%eA��s/����� _W�i��j�&��B�"��'�W6}-�5��Qr g�b��������N�*�T��U�!)������p�
��:�mi�T�C��I
M*���Q���WUB�X�@�,"L�S�RYM�-�NV�N6I_���rq�H0C'IP�	U�L��8��
�q>�YYv�g���+�u�oX�<�	h &Ez(0&���)S9�J(-�(�����(��8����P�s(���J"��Qv�I@LoZPN�%6Q8�_�C�|}�2������ H��Y���B�	�k[k&i"MLYj�,�������9�^��sR�����p5L-ca�5D������v��t�Z@����� �AP�x���s��8O`*�ae����nrl12���M��w:�7���Mt�[3<x%��p��Q�P8�c�Hci�� 3<��G�Vh1���iQ7%%0���y���L���l:�v6�T��vV�S����:e�n�r��D6�eN����U@��e��NA#����qJ��\'��
"��b�,�"�;3��V��n\O���
^g ��|L�";1Zu�9@�R$*��F��mRSh[��Bg���������*P�(t����P�	��I"��6���9NN95prA)��P��S,�,6�����8�j8S�u�u�<'���W	��k��M�S���������%�N�a�:�P�) l
2	.��M�[������J�������C�r�r,t�M�0)(���S�Rvg���0b�Tq]�tJJh����	V�pv���(C��X��r�U+�����Ts�L�����8�&�7�'��YN9J�&7(
�C��b1+�
�M�,T�^�d'B�����E8�V@������;�T��� W����N��.q����QT����J�D���n������yOu2�M�t�V���*	���#�,���*aC9�����F~}��M�
6�@wp)T��i����"e����������K�7��#�u��bC&�U���So(Y��H��\��$�EfQl��	��XY_��P�	�
�l�"�L]Wi,��3U�d���V���\?dX����q a-�3#������
	��Hi�����L����:��U���W*o���UnL$UrP����0�R}2l!Td���s�3u��'������q%�7Uo�����)?P���eO�-��|K����uR����7G�@u;�����T!Og5���G��P��dF0�FU*��J�=/��3��r��	6+
��j�R��0�$X':{N%���Eh[8Y�R7*)��L��6�
UEYAL�K�0Ra!��n���3H�Q�wB4��ey"jX�Sp���z��q`���E�*G<P,!8���-�=f�*�!������*�
fJJQ�������*��c�'E��8S���g_n9n��5���
�t������+h��y�"*��q��:�����Wp}W�85D���$������u��Rd��������X���}���K�R��)V!�*��CEK0�QW:q�c�K�����,Hl�f%u�p<�|�^b�vc^�B�fE����su>$�t�'2F:R�gJ����!c��r���PR����-J�]�c��O0��u�$�DI7�Z��sdu`'�����	��n���U�.*W��B�����3X���J>�|$D��>�t����
B��)��U������^tT�/�KTreKj>8��(��Z�����(r�un��Q���6�4����%�M�
����|K��\G������P��$�*����t�~*c[~�T��U��:vh�T�%�EA�U�Jt����Q!(�S�r��(U4D�-b	;�K3��*�����:h���#�Uh:���=�U�i�:A�P�.���uv)5E��m"��%u�4����Jz���1�3A��r��/7�7��*�@5�q�U�����x%5����[�\I�;vD�x2.���5 ecqK<�I^���69��qU�Q��<�V�e�pS}v ���:�S/9�UwU�VOs�*g1D��w�7�7�<�Z�P�%�*�
Q�b74*�bB������eQk����91Ai[���)~�����un�b�Y���j���H��L�� }�}Q� �Ve��&��������Jj)�_��O5�m���c�����:�-W�~����5���&5�r�+9�=�^�*��q�#pYk]�v!�c�s��p���U���U������]�J�K��a`����2@-��r���������Q���
&��3�B�XiJ�&+?"���^6-��y�b�R����G�����m!/���U��VG��HY�*�D��Y�&�D��8`�
CT5f�5�I-Z����&�G���+�,`�C�j�s��5���R��QM@�tU�jt����:
�m��R%?+�@5\��g{0�_�I���R���	��t�XzA�&'�,�����*��T�I��������t2���"c_6��U�P-��t(��!t�[E�$,�T�Kyl.T
mU"O��^Bc����";�\���B������H� �Qa(������/�)M�������*��.�?��S�@�� %����Q��B������S�T�p�RtZ�-(v:$��q��L��xI��H�s�
�"�5�i����jHf���D<b�h����y`�V�PV�%\��l�Qa`�{�+������Q��c��Z��f�"\
>�e�Nt)1i0X	�c� w�B�0W���v�`geg�9E��t��4��"A���[9���j��cE������\g2��:�kp���f�����Y6���DEuVYA[�^J��B����2�����R�z����O�vg+k�r8%5����n���B�Ee�i���7m�
��	���P}����5���ZF�)0�*uZ�-UbIA�\'��o�@kG��i�
��C�f��T�8��)(!�jt�%�<\�[��������eF��Mf?��#�*QUl�\���kSI�R5���7P�l$�x���>;>��Hm#XT��*p�\5BD�V�`
,��������QgPN��7���0)��H0J0���uF$����M#U���W��:WM�^$a�ku�
p;�#��������8y5,�Jg��U�����k|p�!��x3���C�����^������z��AT�ib���LP4N�^ZU��0-�MB��8��D��9B�(�W�4�2D�W��^J^'D�tV��t��H�:�����tX���4��NR!A���T���~�<�G���$������U�o�Lm�1,�}!��AL9� a��x^�.��&fa��M"��u�
DoEi%��T��
_:�P$Q�1j����_�m�B:�%���cB�������k��J��Q�x��tb�T�r�eD��@����S�T/��t����F�)����@=N�v��.m8��J��DS1�����h�����t~����{�"V:�H5����+�B$T������V'��d��K�p-5���8"��x��5�=BNZ@�y�'c!�q���02!���i**����*q;t!�\\�e,�@a�Z`����9%E,�\����*]#i��QRP�B	[:(91��<Ah�����GV,������kv��|F�D'�	E@��d��P	�Jd6�'8���poD���RQ��� �-�n��8zJ�u�AVqR�(���d���4�
��Hp��8�y�+'��,ra�Be
���h
��C��i�����T��Q�L�'������%,�����EQ����
mu���;���b��T�B��nS�
�s����WUJ��������x����jK�N
��Q]*Op��I*�u����f�tKo��@\�����0>HS����U�����1s���WP��I�B<;�*�V�Qv��N������8 �,�W�Mp�FIg�E%5�+����/����RV�s��:�P'4e���hP�g�hJ*b!0z��VPl��WD�a���`�t��4��S��S�21,�VpL>d��]O�j���PP.���sj1���8:/��/�h�CtI�D
����������T%���i������bH����B�S�<��\cp���
L��bQ�:��#i����(vD�s�xgQ��]P$��|50�D�����cW+�@�*�E9/M��`�����=�4v�R\���4����T+H2��RU`�r��I1-EL���Nc�V*��,uUC#A'W�Zxl)9��Rp~����:�N����-C�����>B�S���:��d���
�Ho�����h�"�� d���`@m�S*��2����� �:����(t@�������JV]W����z�"7����l9�M��!��Q��iN�V����L�}N��}s���l,��~p��S�k]�c�1Lf�r�6�P*Wm���X��1��c5�������	��,�>e
,�Apy*����3���3��t�M8�s ��t�u����J����l.(�/�=I���z=CY0�t�AgAC�2H�D�;��y�-�dj�W���)�?����oRS)���O��T
�
�Bb0�^����~4�����i���
.Tp�7�������1Bp(TRM�����9|��bs��r��W��-q����U`�!~���C��C-�k���{L����4��3�S���ng��E�,n���?�"DR�a��2t0��!d�
�� �Q�5P��2���[i��d�p���gMg ����M��1<i�Y��)9X�3�������}�0��_�z���.�Q�tT6��.q='9&���[�Ux�C�fr����&}����(a
���Q�F���u����t�Gw���Gcr���r�7�7�n
;�Y��AdM�P�1K���,���|3��@)gEP��
��f�*�W��t��N%l��q������X�K�YOt�(�;7�.��=+4��x�m�^),t�����@B�����9`���`;���u�v�g�Z��e�	��_��2�jc����
N�[C?d(I����+|�<����:N��aMC
���^q=6����y����Ok�+�/-�+�Q�������^|�EH�����R���J�EaE���-�����gM����b�;Z��>��*t���y:������Z����k0��&4U�v=8���6}����lc0p���g;��Q=���S��I4�V\��Xa�1�i�a+<���q�3)�����s��������3}���n�Z	�u�+������$��Q+zF�N���f<a��w�S��vT��X x#�����,3�s"��D�T���X���Y���c�@T[��E�9������t���L_Af|�/;5_��
�!G�E�I��J�&���Z )<F+;�o,g#�~y��N5�g��=�83�&Z�� �~�t@i��P����������,B�gGG��c��Z����Y-T��E��C�����PM�2��<�JN~gEKM=B������T�;)eJ���zM��V�PZ~��a\	�?1�C�`Ee�7{��
�{�'��xQr�k:�,�~��r�|mW1�S�3���f�<��Y[M��^e����jWp����G4���`Ne���d���vaP��;���,�?�t���gV����HO�b��t�	a����������V�h2q<K5�FV]Vs\���2�b4Zh�������7�:J���V3*\F��_Sd��`l�;��R>S�td��<[��d%��}9!qZH ���:��8����!<��{��3���R�7J�C]�14=�-� M�6���O3"�-�D�Q��'����h���I&kN��&��t
e�s�6��=����������zM�g�����-�Z��Z���Y�w�;r5���p����h���V����1�����s�Ku_u���f��i��\E����A*-H�}��UY��V2i).������X���a��V����1L��[q?"�2`�A��u�7U�-���9��������l�������:�*�c���IiF� ������6���ow��ish���1)�m�)&��1�&�zb�lL��y�f�1��xb?����h�2�(S}���"c��>0��
�������NEt�����{���X�nH�f��KW���YG��2�N"k~�����j��1�X"{�.�q�3��<l9��1F����<�Y�����M���n�*P�*t����D�6���|	X�3�������,��c��:������&UJ��)�����"qIF��YB���i(0�BW_��F���������%��V��d���@S %���i�7�"��^�id���yI}u���"�Lf|�����p�DG���tXE4����D�w�o}��������(<��7��p�����+�s��b��������:��J�v`�1RTV���n��A���,�d���E���X:�\.�m�?s��X
�j9��j4
GAQ�S�z%{���"�j�d�.J��[|.fd"f�A�nq���Q��V��+J���~b�s��z��z:2��PP���D�����N9ga�G �Z]�U���m�|�?dT��#��i�n�OseC�@	D3*p�=�8)�������t���xr���I�9�<I����h.Q)��a�b��6W[�j11WLZ�r�n���!�#���J�&su�m�����Y.=��X�@�:��PZ$4/�,��Z�I� m������E.����T�
���)�U���]=�{	{jp��.l�A���KW|:�����rM>�f��Y���$S��:���[u�v�4�eC!+(�-��Ul�*�E����������bk�p4��;�Z�s�7u�xg�I#�M����s���?��,�l�I�B9;����|$��U]v/\D��8�s���0�y*�����6t��]Jb�|\����������GL�����[5�������������:d�
������kCChxh��L������J�����8>�8��j��H����L��k��e$�6������"*�v-R
" �}�[=��U5����2#�W����qN�2�\���F<)�(����OJH.���R"������uB
)��)Q��"v��k{���$j39W�#����|���f(��6|KwF'����J��JUK),���tdC0Y���?[("E���kG.����]�,������p���)���s3�E�CPmv��:
FB57Ft��N�R~)��;$��z0/���foO�z�L�t��px���h�;���R����I����J��_����M4�I��LI=���G�M���}�j^�]���aU90�7���%��M9�u��7����<$as�W3X�!��?,:��Q��$0+�I�/���4����P��\���0���"��Z�3�LKx>&rW��JD���E��>S�i�4&�vCwz!�V]���}�#`w������lg �
�����(KZ�S�b�j��T�������z�C�&��t���Ok:�P{�IuL��4te��E]����7�Sj����4b�*k�� {?��$qB�	��.u���3N[����������k�w2��;�Sh�d`1y"i���JS�n
����I��.�?�
:&�K���3ok����P���RJ�2�2'cwx����u�������+w��v)��a#�+�f4x�wP�����X%���#o�����ZG�]��hU`k��B��W4	C�;��b��R��r���f������`*���x��1���BQ�)>�4Y��.�|k5��W������:1�E��&g!T
�-f�jM���1�1�������=�����I/~�C�p�]���V������ni��G
���=97�>�p���2u�z82Q��Y!��4�&4�������</�ArZ������P�Fm���9q#k����(���B�;��1C8�l��cm�uw��2�5���Cq���@�����MqQu�p��(9h���ER��+"G���4zd35�#��~r4��%kbIH���b���v/q?�x�4y��2mi����n�AA#�%:����b�3yt91�F�0=�NL�YF"F����K�K�6�k	r��8���!�lsWw)hP�d��ip6�l��j�x\��,���_�a�Mo���ds��oF�+�4S6�!�&i��f��]�U5����V�&��U�ZjNo`	3��E�RS[�"��l��&�H����p!b��n0���/H��j�s6��Qs��5����1@H�������s�"�TH��Mu)���h����^c��<�5,��D��$��b�r�����L����a`h�{��M����CI��87&�jj��v������q�`8�m,�n��f�!(��-��X������N��`q���yB�o�h�����<��/�\?r�*��R���'O��]�=<||5
i����p���,������c���X����;���@@��\_�a�v��9�5��9���-�����J�CQ��\���
,��&=@�h�s�����+R���E�eJ��@|��JafJ	!'��{hn�q�|������:�si�1�$l������@p����6�LU����X�
5H��vkh���x���=n��RD#`�b7c�<��},&#WT(8�l�M�]+D*:G�:��	Na��1�.n��_�"�*e
�bY!{E�V�eQ^��rbjF9�<��(���~�@�!wjD�����0��W����~"k�����>��e���D�9x7����X���"��_�����9t�NXG��L�)��z%`Z�����\v|����=�Id�{Bnj�MW�)�1�{�������|��=M�`4duv�Vb��wE��+��Vp���;[���k?GZ#��)���P�Sl
�y�����������y�/���k���B�k�<���F��l$�K����Fa����-���STf���t��los
�P$��8�H���\�����-^5�g~�@p*)��R=�~��:�(���]��A&/X���MX�K��~���~��� 0��03$�Y�����+P�Y�DEb�:�Pl4<C�K����V�,��t�&wM	8��C�FtI1UX�J�������E]��((qB��GpZe�����n����&��z�i{������pn�/���p���G�N���Sf�����J�+��:����6���^z���������l5_o?���+��3tV�{�\�w�b��[KO
l���k
�:��l56bb�>����+^q�OZn���X��fv��g�52j�y�T�q�aD��+���:���-�#�^����X��M
!!���*(rx�N�����q�U&zwS�E3�3%LN�K���R�>�4���g������BE
:�K�rS
����l�uG@���j��~��vh~g��st6q��sr'����7bH7#�������lF�h�;pB�<~s������ABSO�UZ� �2�����q����t�����n�Z�D
�Af�jdD������LfQ�"[5����P��~��t1TUp(C�}���i�'�	���(r�������]�&���UW`�j0�	����G}�k���'��3Kt`}��e����F����Ut=y-:���1�w��)&��� W����Rp��j����kJ4����#������	����q��T7K<�;�X^53��12w�@�
�(��7uS�"it���@��G���A�X�DlC'�	��Xlu�t\���#�&BPh��i�}0���H�FT2��D�(�B=�
�P�r�$q
��g`��X4�N����`�f�w�B.�Q-�>����*�Li���p��6��V*��Y�����:S�^������z1!0D�j�H92n��.�����5a<�����)��?���U��f����Zp�=�d��p��,��\!�@,�����5w���u}k^��>�C5��Mz��� ��^���s+�FyF�b���hF������H�*�v]��*��h�t�W�&6�HEDZ��E���������(@C'��ZpzT7j,���bz��{P�����P���i�VZ��P4k���l5����0���b#�DOG1�a��(�e����uZj���K�3�S�K�V���]���k����B��9�W��J8aZbje�`S�� ��d�H�+:b�w��1C�Z�W��
�T~$����M��z
H;��k���8��&K�g6V����Tb����Qn����h��7J4�>���yt=H�24@�du���C��(�+��I�9�x,}����u06��93�G���%��	�UUq�1O8#�WOh�{3������_�M����-�{����>�Q��S�x��Yt��rt�hk 2B� ��!&��x���6%HJOR����&��*�Zh�)HF�O�7����3�&"������:u��J5���:?gd����usL���AqV�av���E81=,�z��j�j�J_^�5H��F�j�1�������{���:4a��2S:��y=���7�n�2UBCW��������9$M�
�����f7��@t��"������a����5����2���=.G|PyDO
6'p�������88��7h�a�u��hBGr�w���f�3�f���[_�n����
��X]�����������+�i0��V���%�"���
��S��`�	�X�E�+hNNP�9��!De���l�'���Yb�P���5S���S%y�����i�0�D-���W����E���en)�����H�Mw$����<UB��j�V�j���eVN�|���h YG7sF�a������7�_��6�{���3 ��o�	b}@
/�-A�'{H���Xoo��)4T=�G����5������L{��L�3�zI��C�m�L�^�F=*��'���B�3Ct�b����H9����=W'�@2�������r��F{�i�,D����Z���:��i���#]M��D�6���n��2��p��Q��a��@f�r='`8��#�kR�yf�3�H��iDh���@:���m�[������Y@�����!�6H����BZ?`bCP��
f	
oG���f��P�m�U��\<�s���u���kM��(
���m��a���������E-�����lOdYl|�4Z}�OM�>t����Iu��i�u��Q��v�\��9��%��� �E����i%b*��	F^)M��!��b� �	z�w@��U�!���Y�0g
7:�BRbu����V;���E�b'v��S"��PR6�i�[�WS��.H��g�&0��@��8�<l���-B��h�;�V��0G������R�={�;��(F�v���:�����]l�������i5h(�
���|�10��b�*�\�j������b�K����:#���iK��D-���o9����*���f?���Nz�N����4
2��E��(6m�_�Y���tB�*F��*�	e�>t�6�2B�_�B��VM�=��d��<������U2hB�\��~��^�:���<��	�Qg��������l�O��~Ek{r���[��l��'te��o�}�*�3'J���2at�!]��B�	�wc���@n������X�)�nD�9��E�*�S��g�?�lE�z��LS0p�������� gD�P���T�N1*�N��<��9���s�������lF���aU�Dm6��R=q�����	
�tJ�vd��������R������M �	0	"�i����
������c��9��BL��v�(�
@��_Y����D`�]=�9�3�R�5H��c�PI���7/��R���)������%�������S{kVG��a�-4Ctz��.����m�MY��$��i��&�C�SY=�����L������lr(�[�������N*�|�\R���g���K8�����J	e�W��K����S�Z(P�
h��,fM���	4M���:�fQS�)������
�C@��+�H-Ji����|e���PZ{�=(\uf<�0��=�-����)
�_��}�9VQ��U31����l�a�'C������}M��Ba=��H�iDy��[*���6�D$se�C��K�X�e��j3
a0���9O#N�9�-yF]����a*#�Y�fp��WL@���	�`��P�3�g�Fk��t!/��@��3*���h)�JN�����?Th��L�a$��
Mg��p�@����	���r3.	/#���9f���m?_�?=����������\����j�����BR�G�@qn����%W�Nf{.yN>���>���YS����-���c#��`�W�7z~`(�|f��� �y_�2���Z��|�vhb�o�����Zo��pzmSC_�{�HD��h�9c}��|��4JE=�s��x�K(�j>+����h/x ��c"��u��������h��q?3���?
C�&k�`h����6P�|U�lZ ��A<��\
�Uq��	�.�T|���C�[mS���6~[����h���|�{*O�tP�Gu��$���,�`��A1:���%�v�a����Z]< k����E���)5�Z��R%�0Q �	�s��=��j�
Qc����d7�(�W�e���{fv�i�9�������U��@��8�.	�jRP
[r�RIiu�j����Z�z�)y����������{v_�{kh)v][I�"G�!����5a#��S�����%�_m'�
rW&��Q�0Lq����/7:���1��e���GtBk����9Z%M�O6o=%�Wp���b���W�7�w��������R�j_�y�lW=S��x����d�"�v��6�q�����J��u�
_�.��l`��	�R:��q�����-(��Z��(L������=3�($r����5m�����$���]/1]��$
��!E�'�G�����K����]��x�1kb�khk	�i�b��5��tbPu�s��\��>U��v����=:�����g�o<|�����?y����>������p�������}�����>y�_y����O�~s����7�N��5���Oo�>�������^����^{�����^�w��w��������������OS>�~�{�n?�[����������{���{��e~~���7��������^���?y���O���^���O��|����^����z������G_>������=x��?/���{����������]��������{z��OO�^=����~}z������������?:�<y���7�����z���/�>��>y���X?{��?�����{���N�����W^{�����fC����������/q����?�[.���O���W����~���^���n_����_~���1��k��yz��_���o�|�[~��>����7��s�t�w������wz���?�����o����x�����������|�����������}���g���_t�_N�_����������3��>����w������y�����>��g�t��^���zg����+�����s/�J�|3_���
O�z	��%o�g?��#?�}�������_��W������Q��Ko�\�xj?���_t�����|���������������~������y��w�����~��?����������Q��?�k��W��z����+I����M{�=�[+���s�[�����uo�/.{���������!%���6�}3�MK�Q������W��`U���[��k�G���������gZ8��
#41Alexander Korotkov
aekorotkov@gmail.com
In reply to: Alexander Korotkov (#40)
3 attachment(s)
Re: WIP: Access method extendability

Hi!

Patches was rebased against master.

In the attached version of patch access methods get support of pg_dump.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

Attachments:

create-am.5.patchapplication/octet-stream; name=create-am.5.patchDownload
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
new file mode 100644
index d657c20..56344a3
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 20,25 ****
--- 20,26 ----
  #include "catalog/heap.h"
  #include "catalog/index.h"
  #include "catalog/objectaccess.h"
+ #include "catalog/pg_am.h"
  #include "catalog/pg_amop.h"
  #include "catalog/pg_amproc.h"
  #include "catalog/pg_attrdef.h"
*************** static const Oid object_classes[] = {
*** 159,165 ****
  	ExtensionRelationId,		/* OCLASS_EXTENSION */
  	EventTriggerRelationId,		/* OCLASS_EVENT_TRIGGER */
  	PolicyRelationId,			/* OCLASS_POLICY */
! 	TransformRelationId			/* OCLASS_TRANSFORM */
  };
  
  
--- 160,167 ----
  	ExtensionRelationId,		/* OCLASS_EXTENSION */
  	EventTriggerRelationId,		/* OCLASS_EVENT_TRIGGER */
  	PolicyRelationId,			/* OCLASS_POLICY */
! 	TransformRelationId,		/* OCLASS_TRANSFORM */
! 	AccessMethodRelationId		/* OCLASS_AM */
  };
  
  
*************** doDeletion(const ObjectAddress *object, 
*** 1269,1274 ****
--- 1271,1279 ----
  
  		case OCLASS_TRANSFORM:
  			DropTransformById(object->objectId);
+ 
+ 		case OCLASS_AM:
+ 			RemoveAccessMethodById(object->objectId);
  			break;
  
  		default:
*************** getObjectClass(const ObjectAddress *obje
*** 2414,2419 ****
--- 2419,2427 ----
  
  		case TransformRelationId:
  			return OCLASS_TRANSFORM;
+ 
+ 		case AccessMethodRelationId:
+ 			return OCLASS_AM;
  	}
  
  	/* shouldn't get here */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
new file mode 100644
index 0232e0d..01997a9
*** a/src/backend/catalog/objectaddress.c
--- b/src/backend/catalog/objectaddress.c
*************** static const ObjectPropertyType ObjectPr
*** 437,442 ****
--- 437,454 ----
  		Anum_pg_type_typacl,
  		ACL_KIND_TYPE,
  		true
+ 	},
+ 	{
+ 		AccessMethodRelationId,
+ 		AmOidIndexId,
+ 		AMOID,
+ 		AMNAME,
+ 		Anum_pg_am_amname,
+ 		InvalidAttrNumber,
+ 		InvalidAttrNumber,
+ 		InvalidAttrNumber,
+ 		-1,
+ 		true
  	}
  };
  
*************** static ObjectAddress get_object_address_
*** 673,678 ****
--- 685,692 ----
  							   List *objargs, bool missing_ok);
  static ObjectAddress get_object_address_defacl(List *objname, List *objargs,
  						  bool missing_ok);
+ static ObjectAddress get_object_address_am(ObjectType objtype, List *objname,
+ 						bool missing_ok);
  static const ObjectPropertyType *get_object_property_data(Oid class_id);
  
  static void getRelationDescription(StringInfo buffer, Oid relid);
*************** get_object_address(ObjectType objtype, L
*** 912,917 ****
--- 926,934 ----
  				address = get_object_address_defacl(objname, objargs,
  													missing_ok);
  				break;
+ 			case OBJECT_ACCESS_METHOD:
+ 				address = get_object_address_am(objtype, objname, missing_ok);
+ 				break;
  			default:
  				elog(ERROR, "unrecognized objtype: %d", (int) objtype);
  				/* placate compiler, in case it thinks elog might return */
*************** get_object_address_opcf(ObjectType objty
*** 1515,1520 ****
--- 1532,1596 ----
  }
  
  /*
+  * Find the ObjectAddress for an access method.
+  */
+ static ObjectAddress
+ get_object_address_am(ObjectType objtype, List *objname, bool missing_ok)
+ {
+ 	ObjectAddress	address;
+ 	char		   *amname, *catalogname;
+ 	Type		tup;
+ 
+ 	switch (list_length(objname))
+ 	{
+ 		case 1:
+ 			amname = strVal(linitial(objname));
+ 			break;
+ 		case 2:
+ 			catalogname = strVal(linitial(objname));
+ 			amname = strVal(lsecond(objname));
+ 
+ 			/*
+ 			 * We check the catalog name and then ignore it.
+ 			 */
+ 			if (strcmp(catalogname, get_database_name(MyDatabaseId)) != 0)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				  errmsg("cross-database references are not implemented: %s",
+ 						 NameListToString(objname))));
+ 			break;
+ 		default:
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_SYNTAX_ERROR),
+ 				errmsg("improper access method name (too many dotted names): %s",
+ 					   NameListToString(objname))));
+ 			break;
+ 	}
+ 
+ 	address.classId = AccessMethodRelationId;
+ 	address.objectId = InvalidOid;
+ 	address.objectSubId = 0;
+ 
+ 	tup = SearchSysCache1(AMNAME, PointerGetDatum(amname));
+ 	if (!HeapTupleIsValid(tup))
+ 	{
+ 		/* Access method is missing, report error if needed */
+ 		if (!missing_ok)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 					 errmsg("access method \"%s\" does not exist",
+ 							amname)));
+ 		return address;
+ 	}
+ 
+ 	/* Access method is found, return its oid */
+ 	address.objectId = HeapTupleGetOid(tup);
+ 	ReleaseSysCache(tup);
+ 
+ 	return address;
+ }
+ 
+ /*
   * Find the ObjectAddress for an opclass/opfamily member.
   *
   * (The returned address corresponds to a pg_amop/pg_amproc object).
*************** check_object_ownership(Oid roleid, Objec
*** 2178,2183 ****
--- 2254,2260 ----
  			break;
  		case OBJECT_TSPARSER:
  		case OBJECT_TSTEMPLATE:
+ 		case OBJECT_ACCESS_METHOD:
  			/* We treat these object types as being owned by superusers */
  			if (!superuser_arg(roleid))
  				ereport(ERROR,
*************** getObjectDescription(const ObjectAddress
*** 3128,3133 ****
--- 3205,3225 ----
  				break;
  			}
  
+ 		case OCLASS_AM:
+ 			{
+ 				HeapTuple	tup;
+ 
+ 				tup = SearchSysCache1(AMOID,
+ 									  ObjectIdGetDatum(object->objectId));
+ 				if (!HeapTupleIsValid(tup))
+ 					elog(ERROR, "cache lookup failed for access method %u",
+ 						 object->objectId);
+ 				appendStringInfo(&buffer, _("access method %s"),
+ 							NameStr(((Form_pg_am) GETSTRUCT(tup))->amname));
+ 				ReleaseSysCache(tup);
+ 				break;
+ 			}
+ 
  		default:
  			appendStringInfo(&buffer, "unrecognized object %u %u %d",
  							 object->classId,
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
new file mode 100644
index b1ac704..6b3742c
*** a/src/backend/commands/Makefile
--- b/src/backend/commands/Makefile
*************** subdir = src/backend/commands
*** 12,18 ****
  top_builddir = ../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o  \
  	collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
  	dbcommands.o define.o discard.o dropcmds.o \
  	event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
--- 12,18 ----
  top_builddir = ../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
  	collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
  	dbcommands.o define.o discard.o dropcmds.o \
  	event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
new file mode 100644
index ...0bec2a8
*** a/src/backend/commands/amcmds.c
--- b/src/backend/commands/amcmds.c
***************
*** 0 ****
--- 1,193 ----
+ /*-------------------------------------------------------------------------
+  *
+  * amcmds.c
+  *	  Routines for SQL commands that manipulate access methods.
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *
+  * IDENTIFICATION
+  *	  src/backend/commands/amcmds.c
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "access/heapam.h"
+ #include "access/htup_details.h"
+ #include "access/xact.h"
+ #include "catalog/binary_upgrade.h"
+ #include "catalog/catalog.h"
+ #include "catalog/dependency.h"
+ #include "catalog/heap.h"
+ #include "catalog/indexing.h"
+ #include "catalog/objectaccess.h"
+ #include "catalog/pg_authid.h"
+ #include "catalog/pg_am.h"
+ #include "catalog/pg_collation.h"
+ #include "catalog/pg_constraint.h"
+ #include "catalog/pg_depend.h"
+ #include "catalog/pg_enum.h"
+ #include "catalog/pg_language.h"
+ #include "catalog/pg_namespace.h"
+ #include "catalog/pg_proc.h"
+ #include "catalog/pg_proc_fn.h"
+ #include "catalog/pg_range.h"
+ #include "catalog/pg_type.h"
+ #include "catalog/pg_type_fn.h"
+ #include "commands/dbcommands.h"
+ #include "commands/defrem.h"
+ #include "commands/tablecmds.h"
+ #include "commands/typecmds.h"
+ #include "executor/executor.h"
+ #include "miscadmin.h"
+ #include "nodes/makefuncs.h"
+ #include "optimizer/planner.h"
+ #include "optimizer/var.h"
+ #include "parser/parse_coerce.h"
+ #include "parser/parse_collate.h"
+ #include "parser/parse_expr.h"
+ #include "parser/parse_func.h"
+ #include "parser/parse_type.h"
+ #include "utils/acl.h"
+ #include "utils/builtins.h"
+ #include "utils/fmgroids.h"
+ #include "utils/lsyscache.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ #include "utils/snapmgr.h"
+ #include "utils/syscache.h"
+ #include "utils/tqual.h"
+ 
+ /*
+  * Convert a handler function name passed from the parser to an Oid.
+  */
+ static Oid
+ lookup_am_handler_func(List *handler_name)
+ {
+ 	Oid			handlerOid;
+ 	Oid			funcargtypes[1] = {INTERNALOID};
+ 
+ 	if (handler_name == NIL)
+ 		return InvalidOid;
+ 
+ 	/* handlers have no arguments */
+ 	handlerOid = LookupFuncName(handler_name, 1, funcargtypes, false);
+ 
+ 	/* check that handler has correct return type */
+ 	if (get_func_rettype(handlerOid) != INDEX_AM_HANDLEROID)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 				 errmsg("function %s must return type \"index_am_handler\"",
+ 						NameListToString(handler_name))));
+ 
+ 	return handlerOid;
+ }
+ 
+ 
+ /*
+  * DefineAccessMethod
+  *		Registers a new access method.
+  */
+ ObjectAddress
+ CreateAccessMethod(CreateAmStmt *stmt)
+ {
+ 	Relation		rel;
+ 	ObjectAddress	myself;
+ 	ObjectAddress	referenced;
+ 	Oid				amoid;
+ 	Oid				amhandler;
+ 	bool			nulls[Natts_pg_am];
+ 	Datum			values[Natts_pg_am];
+ 	HeapTuple		tup;
+ 
+ 	rel = heap_open(AccessMethodRelationId, RowExclusiveLock);
+ 
+ 	/* Must be super user */
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 			errmsg("permission denied to create access method \"%s\"",
+ 				   stmt->amname),
+ 			errhint("Must be superuser to create access method.")));
+ 
+ 	/* Check if name is busy */
+ 	amoid = GetSysCacheOid1(AMNAME, CStringGetDatum(stmt->amname));
+ 	if (OidIsValid(amoid))
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_DUPLICATE_OBJECT),
+ 				 errmsg("access method \"%s\" already exists", stmt->amname)));
+ 	}
+ 
+ 	/* Get handler function oid */
+ 	amhandler = lookup_am_handler_func(stmt->handler_name);
+ 	if (!OidIsValid(amhandler))
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
+ 				 errmsg("handler function \"%s\" is undefined",
+ 						NameListToString(stmt->handler_name))));
+ 	}
+ 
+ 	/*
+ 	 * Insert tuple into pg_am.
+ 	 */
+ 	memset(values, 0, sizeof(values));
+ 	memset(nulls, false, sizeof(nulls));
+ 
+ 	values[Anum_pg_am_amname - 1] =
+ 		DirectFunctionCall1(namein, CStringGetDatum(stmt->amname));
+ 	values[Anum_pg_am_amhandler - 1] = ObjectIdGetDatum(amhandler);
+ 
+ 	tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+ 
+ 	amoid = simple_heap_insert(rel, tup);
+ 	CatalogUpdateIndexes(rel, tup);
+ 	heap_freetuple(tup);
+ 
+ 	myself.classId = AccessMethodRelationId;
+ 	myself.objectId = amoid;
+ 	myself.objectSubId = 0;
+ 
+ 	/* Record dependecy on handler function */
+ 	referenced.classId = ProcedureRelationId;
+ 	referenced.objectId = amhandler;
+ 	referenced.objectSubId = 0;
+ 
+ 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ 
+ 	recordDependencyOnCurrentExtension(&myself, false);
+ 
+ 	heap_close(rel, RowExclusiveLock);
+ 
+ 	return myself;
+ }
+ 
+ /*
+  * Guts of access method deletion.
+  */
+ void
+ RemoveAccessMethodById(Oid amOid)
+ {
+ 	Relation	relation;
+ 	HeapTuple	tup;
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 errmsg("must be superuser to drop access methods")));
+ 
+ 	relation = heap_open(AccessMethodRelationId, RowExclusiveLock);
+ 
+ 	tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
+ 	if (!HeapTupleIsValid(tup))
+ 		elog(ERROR, "cache lookup failed for access method %u", amOid);
+ 
+ 	simple_heap_delete(relation, &tup->t_self);
+ 
+ 	ReleaseSysCache(tup);
+ 
+ 	heap_close(relation, RowExclusiveLock);
+ }
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
new file mode 100644
index 9e32f8d..3f52ad8
*** a/src/backend/commands/event_trigger.c
--- b/src/backend/commands/event_trigger.c
*************** typedef enum
*** 86,91 ****
--- 86,92 ----
  
  /* XXX merge this with ObjectTypeMap? */
  static event_trigger_support_data event_trigger_support[] = {
+ 	{"ACCESS METHOD", true},
  	{"AGGREGATE", true},
  	{"CAST", true},
  	{"CONSTRAINT", true},
*************** EventTriggerSupportsObjectType(ObjectTyp
*** 1078,1083 ****
--- 1079,1085 ----
  		case OBJECT_EVENT_TRIGGER:
  			/* no support for event triggers on event triggers */
  			return false;
+ 		case OBJECT_ACCESS_METHOD:
  		case OBJECT_AGGREGATE:
  		case OBJECT_AMOP:
  		case OBJECT_AMPROC:
*************** EventTriggerSupportsObjectClass(ObjectCl
*** 1167,1172 ****
--- 1169,1175 ----
  		case OCLASS_DEFACL:
  		case OCLASS_EXTENSION:
  		case OCLASS_POLICY:
+ 		case OCLASS_AM:
  			return true;
  	}
  
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
new file mode 100644
index 8a66196..8bd2658
*** a/src/backend/commands/opclasscmds.c
--- b/src/backend/commands/opclasscmds.c
*************** DefineOpClass(CreateOpClassStmt *stmt)
*** 678,683 ****
--- 678,689 ----
  	myself.objectId = opclassoid;
  	myself.objectSubId = 0;
  
+ 	/* dependency on access method */
+ 	referenced.classId = AccessMethodRelationId;
+ 	referenced.objectId = amoid;
+ 	referenced.objectSubId = 0;
+ 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ 
  	/* dependency on namespace */
  	referenced.classId = NamespaceRelationId;
  	referenced.objectId = namespaceoid;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
new file mode 100644
index a8b79fa..5678058
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copyCreateTransformStmt(const CreateTra
*** 3826,3831 ****
--- 3826,3842 ----
  	return newnode;
  }
  
+ static CreateAmStmt *
+ _copyCreateAmStmt(const CreateAmStmt *from)
+ {
+ 	CreateAmStmt *newnode = makeNode(CreateAmStmt);
+ 
+ 	COPY_STRING_FIELD(amname);
+ 	COPY_NODE_FIELD(handler_name);
+ 
+ 	return newnode;
+ }
+ 
  static CreateTrigStmt *
  _copyCreateTrigStmt(const CreateTrigStmt *from)
  {
*************** copyObject(const void *from)
*** 4789,4794 ****
--- 4800,4808 ----
  		case T_CreateTransformStmt:
  			retval = _copyCreateTransformStmt(from);
  			break;
+ 		case T_CreateAmStmt:
+ 			retval = _copyCreateAmStmt(from);
+ 			break;
  		case T_CreateTrigStmt:
  			retval = _copyCreateTrigStmt(from);
  			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
new file mode 100644
index 08ccc0d..7fd4896
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
*************** _equalCreateTransformStmt(const CreateTr
*** 1835,1840 ****
--- 1835,1849 ----
  }
  
  static bool
+ _equalCreateAmStmt(const CreateAmStmt *a, const CreateAmStmt *b)
+ {
+ 	COMPARE_STRING_FIELD(amname);
+ 	COMPARE_NODE_FIELD(handler_name);
+ 
+ 	return true;
+ }
+ 
+ static bool
  _equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b)
  {
  	COMPARE_STRING_FIELD(trigname);
*************** equal(const void *a, const void *b)
*** 3119,3124 ****
--- 3128,3136 ----
  		case T_CreateTransformStmt:
  			retval = _equalCreateTransformStmt(a, b);
  			break;
+ 		case T_CreateAmStmt:
+ 			retval = _equalCreateAmStmt(a, b);
+ 			break;
  		case T_CreateTrigStmt:
  			retval = _equalCreateTrigStmt(a, b);
  			break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
new file mode 100644
index b307b48..2b99503
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
*************** static Node *makeRecursiveViewSelect(cha
*** 263,269 ****
  		DeallocateStmt PrepareStmt ExecuteStmt
  		DropOwnedStmt ReassignOwnedStmt
  		AlterTSConfigurationStmt AlterTSDictionaryStmt
! 		CreateMatViewStmt RefreshMatViewStmt
  
  %type <node>	select_no_parens select_with_parens select_clause
  				simple_select values_clause
--- 263,269 ----
  		DeallocateStmt PrepareStmt ExecuteStmt
  		DropOwnedStmt ReassignOwnedStmt
  		AlterTSConfigurationStmt AlterTSDictionaryStmt
! 		CreateMatViewStmt RefreshMatViewStmt CreateAmStmt DropAmStmt
  
  %type <node>	select_no_parens select_with_parens select_clause
  				simple_select values_clause
*************** static Node *makeRecursiveViewSelect(cha
*** 604,610 ****
  	LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
  	LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
  
! 	MAPPING MATCH MATERIALIZED MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
  
  	NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NONE
  	NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
--- 604,610 ----
  	LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
  	LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
  
! 	MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE
  
  	NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NONE
  	NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
*************** stmt :
*** 789,794 ****
--- 789,795 ----
  			| CommentStmt
  			| ConstraintsSetStmt
  			| CopyStmt
+ 			| CreateAmStmt
  			| CreateAsStmt
  			| CreateAssertStmt
  			| CreateCastStmt
*************** stmt :
*** 823,828 ****
--- 824,830 ----
  			| DeleteStmt
  			| DiscardStmt
  			| DoStmt
+ 			| DropAmStmt
  			| DropAssertStmt
  			| DropCastStmt
  			| DropFdwStmt
*************** row_security_cmd:
*** 4708,4713 ****
--- 4710,4763 ----
  
  /*****************************************************************************
   *
+  *		QUERY:
+  *             CREATE ACCESS METHOD name HANDLER handler_name
+  *
+  *****************************************************************************/
+ 
+ CreateAmStmt: CREATE ACCESS METHOD name HANDLER handler_name
+ 				{
+ 					CreateAmStmt *n = makeNode(CreateAmStmt);
+ 					n->amname = $4;
+ 					n->handler_name = $6;
+ 					$$ = (Node *) n;
+ 				}
+ 		;
+ 
+ /*****************************************************************************
+  *
+  *		QUERY :
+  *				DROP ACCESS METHOD name
+  *
+  ****************************************************************************/
+ 
+ DropAmStmt: DROP ACCESS METHOD name opt_drop_behavior
+ 				{
+ 					DropStmt *n = makeNode(DropStmt);
+ 					n->removeType = OBJECT_ACCESS_METHOD;
+ 					n->objects = list_make1(list_make1(makeString($4)));
+ 					n->arguments = NIL;
+ 					n->missing_ok = false;
+ 					n->behavior = $5;
+ 					n->concurrent = false;
+ 					$$ = (Node *) n;
+ 				}
+ 				|  DROP ACCESS METHOD IF_P EXISTS name opt_drop_behavior
+ 				{
+ 					DropStmt *n = makeNode(DropStmt);
+ 					n->removeType = OBJECT_ACCESS_METHOD;
+ 					n->objects = list_make1(list_make1(makeString($6)));
+ 					n->arguments = NIL;
+ 					n->missing_ok = true;
+ 					n->behavior = $7;
+ 					n->concurrent = false;
+ 					$$ = (Node *) n;
+ 				}
+ 		;
+ 
+ 
+ /*****************************************************************************
+  *
   *		QUERIES :
   *				CREATE TRIGGER ...
   *				DROP TRIGGER ...
*************** unreserved_keyword:
*** 13778,13783 ****
--- 13828,13834 ----
  			| MATCH
  			| MATERIALIZED
  			| MAXVALUE
+ 			| METHOD
  			| MINUTE_P
  			| MINVALUE
  			| MODE
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
new file mode 100644
index 045f7f0..4d0aac9
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
*************** ProcessUtilitySlow(Node *parsetree,
*** 1520,1525 ****
--- 1520,1529 ----
  				address = ExecSecLabelStmt((SecLabelStmt *) parsetree);
  				break;
  
+ 			case T_CreateAmStmt:
+ 				address = CreateAccessMethod((CreateAmStmt *) parsetree);
+ 				break;
+ 
  			default:
  				elog(ERROR, "unrecognized node type: %d",
  					 (int) nodeTag(parsetree));
*************** CreateCommandTag(Node *parsetree)
*** 2160,2165 ****
--- 2164,2172 ----
  				case OBJECT_TRANSFORM:
  					tag = "DROP TRANSFORM";
  					break;
+ 				case OBJECT_ACCESS_METHOD:
+ 					tag = "DROP ACCESS METHOD";
+ 					break;
  				default:
  					tag = "???";
  			}
*************** CreateCommandTag(Node *parsetree)
*** 2256,2261 ****
--- 2263,2271 ----
  				case OBJECT_COLLATION:
  					tag = "CREATE COLLATION";
  					break;
+ 				case OBJECT_ACCESS_METHOD:
+ 					tag = "CREATE ACCESS METHOD";
+ 					break;
  				default:
  					tag = "???";
  			}
*************** CreateCommandTag(Node *parsetree)
*** 2519,2524 ****
--- 2529,2538 ----
  			tag = "ALTER POLICY";
  			break;
  
+ 		case T_CreateAmStmt:
+ 			tag = "CREATE ACCESS METHOD";
+ 			break;
+ 
  		case T_PrepareStmt:
  			tag = "PREPARE";
  			break;
*************** GetCommandLogLevel(Node *parsetree)
*** 3076,3081 ****
--- 3090,3099 ----
  			lev = LOGSTMT_DDL;
  			break;
  
+ 		case T_CreateAmStmt:
+ 			lev = LOGSTMT_DDL;
+ 			break;
+ 
  			/* already-planned queries */
  		case T_PlannedStmt:
  			{
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
new file mode 100644
index 46c95b0..516af92
*** a/src/backend/utils/adt/selfuncs.c
--- b/src/backend/utils/adt/selfuncs.c
*************** string_to_bytea_const(const char *str, s
*** 6012,6032 ****
   *-------------------------------------------------------------------------
   */
  
! /*
!  * deconstruct_indexquals is a simple function to examine the indexquals
!  * attached to a proposed IndexPath.  It returns a list of IndexQualInfo
!  * structs, one per qual expression.
!  */
! typedef struct
! {
! 	RestrictInfo *rinfo;		/* the indexqual itself */
! 	int			indexcol;		/* zero-based index column number */
! 	bool		varonleft;		/* true if index column is on left of qual */
! 	Oid			clause_op;		/* qual's operator OID, if relevant */
! 	Node	   *other_operand;	/* non-index operand of qual's operator */
! } IndexQualInfo;
! 
! static List *
  deconstruct_indexquals(IndexPath *path)
  {
  	List	   *result = NIL;
--- 6012,6018 ----
   *-------------------------------------------------------------------------
   */
  
! List *
  deconstruct_indexquals(IndexPath *path)
  {
  	List	   *result = NIL;
*************** orderby_operands_eval_cost(PlannerInfo *
*** 6176,6210 ****
  	return qual_arg_cost;
  }
  
! /*
!  * genericcostestimate is a general-purpose estimator that can be used for
!  * most index types.  In some cases we use genericcostestimate as the base
!  * code and then incorporate additional index-type-specific knowledge in
!  * the type-specific calling function.  To avoid code duplication, we make
!  * genericcostestimate return a number of intermediate values as well as
!  * its preliminary estimates of the output cost values.  The GenericCosts
!  * struct includes all these values.
!  *
!  * Callers should initialize all fields of GenericCosts to zero.  In addition,
!  * they can set numIndexTuples to some positive value if they have a better
!  * than default way of estimating the number of leaf index tuples visited.
!  */
! typedef struct
! {
! 	/* These are the values the cost estimator must return to the planner */
! 	Cost		indexStartupCost;		/* index-related startup cost */
! 	Cost		indexTotalCost; /* total index-related scan cost */
! 	Selectivity indexSelectivity;		/* selectivity of index */
! 	double		indexCorrelation;		/* order correlation of index */
! 
! 	/* Intermediate values we obtain along the way */
! 	double		numIndexPages;	/* number of leaf pages visited */
! 	double		numIndexTuples; /* number of leaf tuples visited */
! 	double		spc_random_page_cost;	/* relevant random_page_cost value */
! 	double		num_sa_scans;	/* # indexscans from ScalarArrayOps */
! } GenericCosts;
! 
! static void
  genericcostestimate(PlannerInfo *root,
  					IndexPath *path,
  					double loop_count,
--- 6162,6168 ----
  	return qual_arg_cost;
  }
  
! void
  genericcostestimate(PlannerInfo *root,
  					IndexPath *path,
  					double loop_count,
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
new file mode 100644
index f798b15..1acd91a
*** a/src/bin/pg_dump/common.c
--- b/src/bin/pg_dump/common.c
*************** getSchemaData(Archive *fout, int *numTab
*** 98,103 ****
--- 98,104 ----
  	int			numProcLangs;
  	int			numCasts;
  	int			numTransforms;
+ 	int			numAccessMethods;
  	int			numOpclasses;
  	int			numOpfamilies;
  	int			numConversions;
*************** getSchemaData(Archive *fout, int *numTab
*** 169,174 ****
--- 170,179 ----
  	oprinfoindex = buildIndexArray(oprinfo, numOperators, sizeof(OprInfo));
  
  	if (g_verbose)
+ 		write_msg(NULL, "reading user-defined access methods\n");
+ 	getAccessMethods(fout, &numAccessMethods);
+ 
+ 	if (g_verbose)
  		write_msg(NULL, "reading user-defined operator classes\n");
  	getOpclasses(fout, &numOpclasses);
  
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
new file mode 100644
index aa01d6a..2341cf0
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
*************** static void dumpFunc(Archive *fout, Func
*** 173,178 ****
--- 173,179 ----
  static void dumpCast(Archive *fout, CastInfo *cast);
  static void dumpTransform(Archive *fout, TransformInfo *transform);
  static void dumpOpr(Archive *fout, OprInfo *oprinfo);
+ static void dumpAccessMethod(Archive *fout, AccessMethodInfo *oprinfo);
  static void dumpOpclass(Archive *fout, OpclassInfo *opcinfo);
  static void dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo);
  static void dumpCollation(Archive *fout, CollInfo *convinfo);
*************** getConversions(Archive *fout, int *numCo
*** 4101,4106 ****
--- 4102,4183 ----
  }
  
  /*
+  * getAccessMethods:
+  *	  read all user-defined access methods in the system catalogs and return
+  *    them in the AccessMethodInfo* structure
+  *
+  *	numAccessMethods is set to the number of access methods read in
+  */
+ AccessMethodInfo *
+ getAccessMethods(Archive *fout, int *numAccessMethods)
+ {
+ 	DumpOptions *dopt = fout->dopt;
+ 	PGresult   *res;
+ 	int			ntups;
+ 	int			i;
+ 	PQExpBuffer query;
+ 	AccessMethodInfo *aminfo;
+ 	int			i_tableoid;
+ 	int			i_oid;
+ 	int			i_amname;
+ 	int			i_amhandler;
+ 
+ 	/* Before 9.6, there are no user-defined access methods */
+ 	if (fout->remoteVersion < 90600)
+ 	{
+ 		*numAccessMethods = 0;
+ 		return NULL;
+ 	}
+ 
+ 	query = createPQExpBuffer();
+ 
+ 	/* Make sure we are in proper schema */
+ 	selectSourceSchema(fout, "pg_catalog");
+ 
+ 	/*
+ 	 * Select only user-defined access methods assuming all built-in access
+ 	 * methods have oid < 10000.
+ 	 */
+ 	appendPQExpBuffer(query, "SELECT tableoid, oid, amname, "
+ 					  "amhandler::pg_catalog.regproc AS amhandler "
+ 					  "FROM pg_am "
+ 					  "WHERE oid >= 10000");
+ 
+ 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+ 
+ 	ntups = PQntuples(res);
+ 	*numAccessMethods = ntups;
+ 
+ 	aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
+ 
+ 	i_tableoid = PQfnumber(res, "tableoid");
+ 	i_oid = PQfnumber(res, "oid");
+ 	i_amname = PQfnumber(res, "amname");
+ 	i_amhandler = PQfnumber(res, "amhandler");
+ 
+ 	for (i = 0; i < ntups; i++)
+ 	{
+ 		aminfo[i].dobj.objType = DO_ACCESS_METHOD;
+ 		aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ 		aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ 		AssignDumpId(&aminfo[i].dobj);
+ 		aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
+ 		aminfo[i].dobj.namespace = NULL;
+ 		aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
+ 
+ 		/* Decide whether we want to dump it */
+ 		selectDumpableObject(&(aminfo[i].dobj), dopt);
+ 	}
+ 
+ 	PQclear(res);
+ 
+ 	destroyPQExpBuffer(query);
+ 
+ 	return aminfo;
+ }
+ 
+ 
+ /*
   * getOpclasses:
   *	  read all opclasses in the system catalogs and return them in the
   * OpclassInfo* structure
*************** dumpDumpableObject(Archive *fout, Dumpab
*** 8405,8410 ****
--- 8482,8490 ----
  		case DO_OPERATOR:
  			dumpOpr(fout, (OprInfo *) dobj);
  			break;
+ 		case DO_ACCESS_METHOD:
+ 			dumpAccessMethod(fout, (AccessMethodInfo *) dobj);
+ 			break;
  		case DO_OPCLASS:
  			dumpOpclass(fout, (OpclassInfo *) dobj);
  			break;
*************** convertTSFunction(Archive *fout, Oid fun
*** 11443,11448 ****
--- 11523,11581 ----
  	return result;
  }
  
+ /*
+  * dumpAccessMethod
+  *	  write out a single access method definition
+  */
+ static void
+ dumpAccessMethod(Archive *fout, AccessMethodInfo *aminfo)
+ {
+ 	DumpOptions *dopt = fout->dopt;
+ 	PQExpBuffer q;
+ 	PQExpBuffer delq;
+ 	PQExpBuffer labelq;
+ 	char	   *qamname;
+ 
+ 	/* Skip if not to be dumped */
+ 	if (!aminfo->dobj.dump || dopt->dataOnly)
+ 		return;
+ 
+ 	q = createPQExpBuffer();
+ 	delq = createPQExpBuffer();
+ 	labelq = createPQExpBuffer();
+ 
+ 	qamname = pg_strdup(fmtId(aminfo->dobj.name));
+ 
+ 	appendPQExpBuffer(q, "CREATE ACCESS METHOD %s HANDLER %s;\n",
+ 					  qamname, aminfo->amhandler);
+ 
+ 	appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
+ 					  qamname);
+ 
+ 	appendPQExpBuffer(labelq, "ACCESS METHOD %s",
+ 					  qamname);
+ 
+ 	ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
+ 				 aminfo->dobj.name,
+ 				 NULL,
+ 				 NULL,
+ 				 "",
+ 				 false, "ACCESS METHOD", SECTION_PRE_DATA,
+ 				 q->data, delq->data, NULL,
+ 				 NULL, 0,
+ 				 NULL, NULL);
+ 
+ 	/* Dump Access Method Comments */
+ 	dumpComment(fout, labelq->data,
+ 				NULL, "",
+ 				aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
+ 
+ 	free(qamname);
+ 
+ 	destroyPQExpBuffer(q);
+ 	destroyPQExpBuffer(delq);
+ 	destroyPQExpBuffer(labelq);
+ }
  
  /*
   * dumpOpclass
*************** addBoundaryDependencies(DumpableObject *
*** 16224,16229 ****
--- 16357,16363 ----
  			case DO_FUNC:
  			case DO_AGG:
  			case DO_OPERATOR:
+ 			case DO_ACCESS_METHOD:
  			case DO_OPCLASS:
  			case DO_OPFAMILY:
  			case DO_COLLATION:
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
new file mode 100644
index 78b2584..3046feb
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
*************** typedef enum
*** 48,53 ****
--- 48,54 ----
  	DO_FUNC,
  	DO_AGG,
  	DO_OPERATOR,
+ 	DO_ACCESS_METHOD,
  	DO_OPCLASS,
  	DO_OPFAMILY,
  	DO_COLLATION,
*************** typedef enum
*** 77,83 ****
  	DO_POST_DATA_BOUNDARY,
  	DO_EVENT_TRIGGER,
  	DO_REFRESH_MATVIEW,
! 	DO_POLICY
  } DumpableObjectType;
  
  typedef struct _dumpableObject
--- 78,84 ----
  	DO_POST_DATA_BOUNDARY,
  	DO_EVENT_TRIGGER,
  	DO_REFRESH_MATVIEW,
! 	DO_POLICY,
  } DumpableObjectType;
  
  typedef struct _dumpableObject
*************** typedef struct _oprInfo
*** 167,172 ****
--- 168,179 ----
  	Oid			oprcode;
  } OprInfo;
  
+ typedef struct _accessMethodInfo
+ {
+ 	DumpableObject dobj;
+ 	char	   *amhandler;
+ } AccessMethodInfo;
+ 
  typedef struct _opclassInfo
  {
  	DumpableObject dobj;
*************** extern TypeInfo *getTypes(Archive *fout,
*** 546,551 ****
--- 553,559 ----
  extern FuncInfo *getFuncs(Archive *fout, int *numFuncs);
  extern AggInfo *getAggregates(Archive *fout, int *numAggregates);
  extern OprInfo *getOperators(Archive *fout, int *numOperators);
+ extern AccessMethodInfo *getAccessMethods(Archive *fout, int *numAccessMethods);
  extern OpclassInfo *getOpclasses(Archive *fout, int *numOpclasses);
  extern OpfamilyInfo *getOpfamilies(Archive *fout, int *numOpfamilies);
  extern CollInfo *getCollations(Archive *fout, int *numCollations);
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
new file mode 100644
index b0f6ace..b03561a
*** a/src/bin/pg_dump/pg_dump_sort.c
--- b/src/bin/pg_dump/pg_dump_sort.c
*************** static const int oldObjectTypePriority[]
*** 45,50 ****
--- 45,51 ----
  	2,							/* DO_FUNC */
  	3,							/* DO_AGG */
  	3,							/* DO_OPERATOR */
+ 	3,							/* DO_ACCESS_METHOD */
  	4,							/* DO_OPCLASS */
  	4,							/* DO_OPFAMILY */
  	4,							/* DO_COLLATION */
*************** static const int newObjectTypePriority[]
*** 95,100 ****
--- 96,102 ----
  	6,							/* DO_FUNC */
  	7,							/* DO_AGG */
  	8,							/* DO_OPERATOR */
+ 	8,							/* DO_ACCESS_METHOD */
  	9,							/* DO_OPCLASS */
  	9,							/* DO_OPFAMILY */
  	3,							/* DO_COLLATION */
*************** describeDumpableObject(DumpableObject *o
*** 1325,1330 ****
--- 1327,1336 ----
  					 "OPERATOR %s  (ID %d OID %u)",
  					 obj->name, obj->dumpId, obj->catId.oid);
  			return;
+ 		case DO_ACCESS_METHOD:
+ 			snprintf(buf, bufsize,
+ 					 "");
+ 			return;
  		case DO_OPCLASS:
  			snprintf(buf, bufsize,
  					 "OPERATOR CLASS %s  (ID %d OID %u)",
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
new file mode 100644
index 049bf9f..ac16740
*** a/src/include/catalog/dependency.h
--- b/src/include/catalog/dependency.h
*************** typedef enum ObjectClass
*** 153,162 ****
  	OCLASS_EXTENSION,			/* pg_extension */
  	OCLASS_EVENT_TRIGGER,		/* pg_event_trigger */
  	OCLASS_POLICY,				/* pg_policy */
! 	OCLASS_TRANSFORM			/* pg_transform */
  } ObjectClass;
  
! #define LAST_OCLASS		OCLASS_TRANSFORM
  
  
  /* in dependency.c */
--- 153,163 ----
  	OCLASS_EXTENSION,			/* pg_extension */
  	OCLASS_EVENT_TRIGGER,		/* pg_event_trigger */
  	OCLASS_POLICY,				/* pg_policy */
! 	OCLASS_TRANSFORM,			/* pg_transform */
! 	OCLASS_AM,					/* pg_am */
  } ObjectClass;
  
! #define LAST_OCLASS		OCLASS_AM
  
  
  /* in dependency.c */
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
new file mode 100644
index 54f67e9..2ba24df
*** a/src/include/commands/defrem.h
--- b/src/include/commands/defrem.h
*************** extern Datum transformGenericOptions(Oid
*** 137,142 ****
--- 137,146 ----
  						List *options,
  						Oid fdwvalidator);
  
+ /* commands/amcmds.c */
+ extern ObjectAddress CreateAccessMethod(CreateAmStmt *stmt);
+ extern void RemoveAccessMethodById(Oid amOid);
+ 
  /* support routines in commands/define.c */
  
  extern char *defGetString(DefElem *def);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
new file mode 100644
index cf09db4..6fb349f
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
*************** typedef enum NodeTag
*** 381,386 ****
--- 381,387 ----
  	T_CreatePolicyStmt,
  	T_AlterPolicyStmt,
  	T_CreateTransformStmt,
+ 	T_CreateAmStmt,
  
  	/*
  	 * TAGS FOR PARSE TREE NODES (parsenodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
new file mode 100644
index 2fd0629..8421d41
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
*************** typedef struct SetOperationStmt
*** 1379,1384 ****
--- 1379,1385 ----
  
  typedef enum ObjectType
  {
+ 	OBJECT_ACCESS_METHOD,
  	OBJECT_AGGREGATE,
  	OBJECT_AMOP,
  	OBJECT_AMPROC,
*************** typedef struct AlterPolicyStmt
*** 2070,2075 ****
--- 2071,2087 ----
  	Node	   *with_check;		/* the policy's WITH CHECK condition. */
  } AlterPolicyStmt;
  
+ /*----------------------
+  *		Create ACCESS METHOD Statement
+  *----------------------
+  */
+ typedef struct CreateAmStmt
+ {
+ 	NodeTag		type;
+ 	char	   *amname;			/* access method name */
+ 	List	   *handler_name;	/* handler function name */
+ } CreateAmStmt;
+ 
  /* ----------------------
   *		Create TRIGGER Statement
   * ----------------------
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
new file mode 100644
index 6e1e820..7de3404
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
*************** PG_KEYWORD("mapping", MAPPING, UNRESERVE
*** 239,244 ****
--- 239,245 ----
  PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD)
  PG_KEYWORD("materialized", MATERIALIZED, UNRESERVED_KEYWORD)
  PG_KEYWORD("maxvalue", MAXVALUE, UNRESERVED_KEYWORD)
+ PG_KEYWORD("method", METHOD, UNRESERVED_KEYWORD)
  PG_KEYWORD("minute", MINUTE_P, UNRESERVED_KEYWORD)
  PG_KEYWORD("minvalue", MINVALUE, UNRESERVED_KEYWORD)
  PG_KEYWORD("mode", MODE, UNRESERVED_KEYWORD)
diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h
new file mode 100644
index 06fbca7..7fb7466
*** a/src/include/utils/selfuncs.h
--- b/src/include/utils/selfuncs.h
*************** typedef enum
*** 95,100 ****
--- 95,142 ----
  	Pattern_Prefix_None, Pattern_Prefix_Partial, Pattern_Prefix_Exact
  } Pattern_Prefix_Status;
  
+ /*
+  * deconstruct_indexquals is a simple function to examine the indexquals
+  * attached to a proposed IndexPath.  It returns a list of IndexQualInfo
+  * structs, one per qual expression.
+  */
+ typedef struct
+ {
+ 	RestrictInfo *rinfo;		/* the indexqual itself */
+ 	int			indexcol;		/* zero-based index column number */
+ 	bool		varonleft;		/* true if index column is on left of qual */
+ 	Oid			clause_op;		/* qual's operator OID, if relevant */
+ 	Node	   *other_operand;	/* non-index operand of qual's operator */
+ } IndexQualInfo;
+ 
+ /*
+  * genericcostestimate is a general-purpose estimator that can be used for
+  * most index types.  In some cases we use genericcostestimate as the base
+  * code and then incorporate additional index-type-specific knowledge in
+  * the type-specific calling function.  To avoid code duplication, we make
+  * genericcostestimate return a number of intermediate values as well as
+  * its preliminary estimates of the output cost values.  The GenericCosts
+  * struct includes all these values.
+  *
+  * Callers should initialize all fields of GenericCosts to zero.  In addition,
+  * they can set numIndexTuples to some positive value if they have a better
+  * than default way of estimating the number of leaf index tuples visited.
+  */
+ typedef struct
+ {
+ 	/* These are the values the cost estimator must return to the planner */
+ 	Cost		indexStartupCost;		/* index-related startup cost */
+ 	Cost		indexTotalCost; /* total index-related scan cost */
+ 	Selectivity indexSelectivity;		/* selectivity of index */
+ 	double		indexCorrelation;		/* order correlation of index */
+ 
+ 	/* Intermediate values we obtain along the way */
+ 	double		numIndexPages;	/* number of leaf pages visited */
+ 	double		numIndexTuples; /* number of leaf tuples visited */
+ 	double		spc_random_page_cost;	/* relevant random_page_cost value */
+ 	double		num_sa_scans;	/* # indexscans from ScalarArrayOps */
+ } GenericCosts;
+ 
  /* Hooks for plugins to get control when we ask for stats */
  typedef bool (*get_relation_stats_hook_type) (PlannerInfo *root,
  														  RangeTblEntry *rte,
*************** extern double estimate_num_groups(Planne
*** 191,196 ****
--- 233,244 ----
  extern Selectivity estimate_hash_bucketsize(PlannerInfo *root, Node *hashkey,
  						 double nbuckets);
  
+ extern List *deconstruct_indexquals(IndexPath *path);
+ extern void genericcostestimate(PlannerInfo *root, IndexPath *path,
+ 								double loop_count,
+ 								List *qinfos,
+ 								GenericCosts *costs);
+ 
  /* Functions in array_selfuncs.c */
  
  extern Selectivity scalararraysel_containment(PlannerInfo *root,
generic-xlog.5.patchapplication/octet-stream; name=generic-xlog.5.patchDownload
diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
new file mode 100644
index c72a1f2..c0e38fd
*** a/src/backend/access/rmgrdesc/Makefile
--- b/src/backend/access/rmgrdesc/Makefile
*************** subdir = src/backend/access/rmgrdesc
*** 8,16 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o gindesc.o gistdesc.o \
! 	   hashdesc.o heapdesc.o mxactdesc.o nbtdesc.o relmapdesc.o \
! 	   replorigindesc.o seqdesc.o smgrdesc.o spgdesc.o \
  	   standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
  
  include $(top_srcdir)/src/backend/common.mk
--- 8,16 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o genericdesc.o \
! 	   gindesc.o gistdesc.o hashdesc.o heapdesc.o mxactdesc.o nbtdesc.o \
! 	   relmapdesc.o replorigindesc.o seqdesc.o smgrdesc.o spgdesc.o \
  	   standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
  
  include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/genericdesc.c b/src/backend/access/rmgrdesc/genericdesc.c
new file mode 100644
index ...46c430e
*** a/src/backend/access/rmgrdesc/genericdesc.c
--- b/src/backend/access/rmgrdesc/genericdesc.c
***************
*** 0 ****
--- 1,31 ----
+ /*-------------------------------------------------------------------------
+  *
+  * genericdesc.c
+  *	  rmgr descriptor routines for access/transam/generic_xlog.c
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *
+  * IDENTIFICATION
+  *	  src/backend/access/rmgrdesc/genericdesc.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/generic_xlog.h"
+ #include "lib/stringinfo.h"
+ #include "storage/relfilenode.h"
+ 
+ void
+ generic_desc(StringInfo buf, XLogReaderState *record)
+ {
+ 	return;
+ }
+ 
+ const char *
+ generic_identify(uint8 info)
+ {
+ 	return "Generic";
+ }
diff --git a/src/backend/access/transam/Makefile b/src/backend/access/transam/Makefile
new file mode 100644
index 94455b2..16fbe47
*** a/src/backend/access/transam/Makefile
--- b/src/backend/access/transam/Makefile
*************** subdir = src/backend/access/transam
*** 12,19 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = clog.o commit_ts.o multixact.o parallel.o rmgr.o slru.o subtrans.o \
! 	timeline.o transam.o twophase.o twophase_rmgr.o varsup.o \
  	xact.o xlog.o xlogarchive.o xlogfuncs.o \
  	xloginsert.o xlogreader.o xlogutils.o
  
--- 12,19 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = clog.o commit_ts.o generic_xlog.o multixact.o parallel.o rmgr.o slru.o \
! 	subtrans.o timeline.o transam.o twophase.o twophase_rmgr.o varsup.o \
  	xact.o xlog.o xlogarchive.o xlogfuncs.o \
  	xloginsert.o xlogreader.o xlogutils.o
  
diff --git a/src/backend/access/transam/generic_xlog.c b/src/backend/access/transam/generic_xlog.c
new file mode 100644
index ...3b5b0dd
*** a/src/backend/access/transam/generic_xlog.c
--- b/src/backend/access/transam/generic_xlog.c
***************
*** 0 ****
--- 1,624 ----
+ /*-------------------------------------------------------------------------
+  *
+  * generic_xlog.c
+  *	  WAL replay logic for generic XLOG.
+  *
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * IDENTIFICATION
+  *			 src/backend/access/transam/generic_xlog.c
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/generic_xlog.h"
+ #include "access/xlogutils.h"
+ #include "miscadmin.h"
+ #include "utils/memutils.h"
+ 
+ #define MAX_REGIONS				256
+ #define MATCH_THRESHOLD			 16
+ #define MAX_GENERIC_XLOG_PAGES	  3
+ 
+ #define	MOVE_FLAG			 0x8000
+ #define	LENGTH_MASK			 0x7FFF
+ 
+ /* #define DEBUG_PRINT */
+ 
+ typedef struct
+ {
+ 	OffsetNumber dstOffset, srcOffset, length;
+ } Region;
+ 
+ typedef struct
+ {
+ 	Buffer	buffer;
+ 	char	image[BLCKSZ];
+ 	Region	regions[MAX_REGIONS];
+ 	int		regionsCount;
+ 	bool	overflow;
+ 	char	data[2 * BLCKSZ];
+ 	int		dataLen;
+ 	bool	fullImage;
+ } PageData;
+ 
+ enum GenericXlogStatus
+ {
+ 	GXLOG_NOT_STARTED,
+ 	GXLOG_LOGGED,
+ 	GXLOG_UNLOGGED
+ };
+ 
+ 
+ static enum GenericXlogStatus	genericXlogStatus = GXLOG_NOT_STARTED;
+ static PageData					pages[MAX_GENERIC_XLOG_PAGES];
+ 
+ static int
+ regionOffsetCmp(const void *a, const void *b)
+ {
+ 	OffsetNumber off = *((const OffsetNumber *)a);
+ 	const Region *r = (const Region *)b;
+ 
+ 	if (off < r->dstOffset)
+ 		return -1;
+ 	else if (off >= r->dstOffset + r->length)
+ 		return 1;
+ 	else
+ 		return 0;
+ }
+ 
+ static void
+ initPageData(PageData *pageData, Buffer buffer)
+ {
+ 	pageData->buffer = buffer;
+ 	memcpy(pageData->image, BufferGetPage(buffer), BLCKSZ);
+ 	pageData->regions[0].srcOffset = 0;
+ 	pageData->regions[0].dstOffset = 0;
+ 	pageData->regions[0].length = BLCKSZ;
+ 	pageData->regionsCount = 1;
+ 	pageData->dataLen = 0;
+ }
+ 
+ static Region *
+ findRegion(PageData *pageData, OffsetNumber offset)
+ {
+ 	Region	*region;
+ 	Assert(offset >= 0 && offset < BLCKSZ);
+ 	region = bsearch(&offset, pageData->regions, pageData->regionsCount,
+ 		sizeof(Region), regionOffsetCmp);
+ 
+ 	/* We should always find region... */
+ 	if (!region)
+ 		elog(ERROR, "Can't find region");
+ 
+ 	return region;
+ }
+ 
+ static void
+ memoryMove(PageData *pageData, OffsetNumber dstOffset, OffsetNumber srcOffset,
+ 	OffsetNumber length)
+ {
+ 	OffsetNumber curOffset, curLength, leftLength = 0;
+ 	Region	newRegions[MAX_REGIONS], *newRegion, *srcRegion, *dstRegionLeft, *dstRegionRight;
+ 	bool leftAdjacent, rightAdjacent;
+ 	int newRegionsCount, leftIndex, rightIndex, shift;
+ #ifdef DEBUG_PRINT
+ 	int i;
+ #endif
+ 
+ 	if (pageData->overflow)
+ 		return;
+ 
+ 	srcRegion = findRegion(pageData, srcOffset);
+ 
+ 	/* Make new regions */
+ 	curOffset = dstOffset;
+ 	curLength = length;
+ 	newRegion = newRegions;
+ 	while (curLength > 0)
+ 	{
+ 		OffsetNumber shift = srcOffset - srcRegion->dstOffset;
+ 
+ 		newRegion->dstOffset = curOffset;
+ 		newRegion->srcOffset = srcRegion->srcOffset + shift;
+ 		newRegion->length = Min(srcRegion->length - shift, curLength);
+ 		srcOffset += newRegion->length;
+ 		curLength -= newRegion->length;
+ 		srcRegion++;
+ 		newRegion++;
+ 	}
+ 	newRegionsCount = newRegion - newRegions;
+ 
+ 	/* Check left region */
+ 	dstRegionLeft = findRegion(pageData, dstOffset > 0 ? dstOffset - 1 : 0);
+ 	newRegion = &newRegions[0];
+ 	shift = dstOffset - dstRegionLeft->dstOffset;
+ 
+ 	if (shift == 0)
+ 	{
+ 		leftAdjacent = true;
+ 	}
+ 	else if (newRegion->srcOffset == dstRegionLeft->srcOffset + shift)
+ 	{
+ 		leftAdjacent = true;
+ 		newRegion->dstOffset -= shift;
+ 		newRegion->srcOffset -= shift;
+ 		newRegion->length += shift;
+ 	}
+ 	else
+ 	{
+ 		leftAdjacent = false;
+ 		leftLength = shift;
+ 	}
+ 	leftIndex = dstRegionLeft - pageData->regions + (leftAdjacent ? 0 : 1);
+ 
+ 	/* Check right region */
+ 	dstRegionRight = findRegion(pageData, dstOffset + length < BLCKSZ ? dstOffset + length : BLCKSZ - 1);
+ 	newRegion = &newRegions[newRegionsCount - 1];
+ 	shift = (dstRegionRight->dstOffset + dstRegionRight->length) -
+ 			(dstOffset + length);
+ 
+ 	if (shift == 0)
+ 	{
+ 		rightAdjacent = true;
+ 	}
+ 	else if (newRegion->srcOffset + newRegion->length + shift ==
+ 			 dstRegionRight->srcOffset + dstRegionRight->length)
+ 	{
+ 		rightAdjacent = true;
+ 		newRegion->length += shift;
+ 	}
+ 	else
+ 	{
+ 		Region *tmp = dstRegionRight;
+ 
+ 		if (!leftAdjacent)
+ 		{
+ 			newRegionsCount++;
+ 			tmp = &newRegions[newRegionsCount - 1];
+ 			*tmp = *dstRegionRight;
+ 			rightAdjacent = true;
+ 		}
+ 		else
+ 		{
+ 			rightAdjacent = false;
+ 		}
+ 		tmp->srcOffset += (tmp->length - shift);
+ 		tmp->dstOffset += (tmp->length - shift);
+ 		tmp->length = shift;
+ 	}
+ 	if (!leftAdjacent)
+ 		dstRegionLeft->length = leftLength;
+ 	rightIndex = dstRegionRight - pageData->regions + (rightAdjacent ? 1 : 0);
+ 
+ 	/* Move data */
+ 	shift = newRegionsCount - (rightIndex - leftIndex);
+ 	if (pageData->regionsCount + shift > MAX_REGIONS)
+ 	{
+ 		initPageData(pageData, pageData->buffer);
+ 		pageData->overflow = true;
+ 		return;
+ 	}
+ 
+ #ifdef DEBUG_PRINT
+ 	for (i = 0; i < pageData->regionsCount; i++)
+ 	{
+ 		Region *region = &pageData->regions[i];
+ 		printf("old %d %d %d %d\n", i, region->dstOffset, region->srcOffset, region->length);
+ 	}
+ 	for (i = 0; i < newRegionsCount; i++)
+ 	{
+ 		Region *region = &newRegions[i];
+ 		printf("new %d %d %d %d\n", i, region->dstOffset, region->srcOffset, region->length);
+ 	}
+ 	printf("idx %d %d\n", leftIndex, rightIndex);
+ #endif
+ 
+ 	memmove(&pageData->regions[rightIndex + shift],
+ 			&pageData->regions[rightIndex],
+ 			sizeof(Region) * (pageData->regionsCount - rightIndex));
+ 	pageData->regionsCount += shift;
+ 	memcpy(&pageData->regions[leftIndex],
+ 		   newRegions,
+ 		   sizeof(Region) * newRegionsCount);
+ }
+ 
+ #ifdef DEBUG_PRINT
+ static void
+ printPageData(PageData *pageData)
+ {
+ 	int i;
+ 	for (i = 0; i < pageData->regionsCount; i++)
+ 	{
+ 		Region *region = &pageData->regions[i];
+ 		printf("cur %d %d %d %d\n", i, region->dstOffset, region->srcOffset, region->length);
+ 	}
+ }
+ 
+ Datum
+ check_move(PG_FUNCTION_ARGS)
+ {
+ 	PageData pageData;
+ 	initPageData(&pageData, false);
+ 	printPageData(&pageData);
+ 	memoryMove(&pageData, 0, 4096, 4096);
+ 	printPageData(&pageData);
+ 	memoryMove(&pageData, 0, 2048, 2048);
+ 	printPageData(&pageData);
+ 	memoryMove(&pageData, 0, 1024, 1024);
+ 	printPageData(&pageData);
+ 	PG_RETURN_VOID();
+ }
+ #endif
+ 
+ static void
+ writeCopyFlagment(PageData *pageData, OffsetNumber length, Pointer source)
+ {
+ 	Pointer ptr = pageData->data + pageData->dataLen;
+ 
+ 	memcpy(ptr, &length, sizeof(OffsetNumber));
+ 	ptr += sizeof(OffsetNumber);
+ 	memcpy(ptr, source, length);
+ 	ptr += length;
+ 
+ 	pageData->dataLen = ptr - pageData->data;
+ }
+ 
+ static void
+ writeMoveFlagment(PageData *pageData, OffsetNumber length, OffsetNumber source)
+ {
+ 	Pointer ptr = pageData->data + pageData->dataLen;
+ 
+ 	length |= MOVE_FLAG;
+ 	memcpy(ptr, &length, sizeof(OffsetNumber));
+ 	ptr += sizeof(OffsetNumber);
+ 	memcpy(ptr, &source, sizeof(OffsetNumber));
+ 	ptr += sizeof(OffsetNumber);
+ 
+ 	pageData->dataLen = ptr - pageData->data;
+ }
+ 
+ #define CHECK_SET \
+ 			if (i - match > MATCH_THRESHOLD) \
+ 			{ \
+ 				if (notMatch < match) \
+ 					writeCopyFlagment(pageData, match - notMatch, page + notMatch); \
+ 				writeMoveFlagment(pageData, i - match, region->srcOffset + regionOffset - (i - match)); \
+ 				notMatch = i; \
+ 			} \
+ 
+ static void
+ writeDifferentialData(PageData *pageData)
+ {
+ 	Page page = BufferGetPage(pageData->buffer);
+ 	Pointer image = pageData->image;
+ 	Region *region = pageData->regions;
+ 	OffsetNumber i, regionOffset = 0;
+ 	OffsetNumber notMatch = 0, match = 0;
+ 
+ 	for (i = 0; i < BLCKSZ; i++)
+ 	{
+ 		if (regionOffset >= region->length)
+ 		{
+ 			CHECK_SET;
+ 			match = i;
+ 			region++;
+ 			regionOffset = 0;
+ 		}
+ 
+ 		if (page[i] != image[region->srcOffset + regionOffset])
+ 		{
+ 			CHECK_SET;
+ 			match = i + 1;
+ 		}
+ 
+ 		regionOffset++;
+ 	}
+ 	CHECK_SET;
+ 	if (notMatch < BLCKSZ)
+ 	{
+ 		writeCopyFlagment(pageData, BLCKSZ - notMatch, page + notMatch);
+ 	}
+ }
+ 
+ void
+ GenericXLogStart(Relation index)
+ {
+ 	int i;
+ 
+ 	if (genericXlogStatus != GXLOG_NOT_STARTED)
+ 		elog(ERROR, "GenericXLogStart: generic xlog is already started");
+ 
+ 	genericXlogStatus = RelationNeedsWAL(index) ? GXLOG_LOGGED : GXLOG_UNLOGGED;
+ 
+ 	for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 	{
+ 		pages[i].buffer = InvalidBuffer;
+ 	}
+ }
+ 
+ Page
+ GenericXLogRegister(Buffer buffer, bool isNew)
+ {
+ 	int block_id;
+ 
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		elog(ERROR, "GenericXLogRegister: generic xlog isn't started");
+ 
+ 	for (block_id = 0; block_id < MAX_GENERIC_XLOG_PAGES; block_id++)
+ 	{
+ 		if (BufferIsInvalid(pages[block_id].buffer))
+ 		{
+ 			initPageData(&pages[block_id], buffer);
+ 			pages[block_id].fullImage = isNew;
+ 			return (Page)pages[block_id].image;
+ 		}
+ 		else if (pages[block_id].buffer == buffer)
+ 		{
+ 			elog(ERROR, "GenericXLogRegister: duplicate buffer %d", buffer);
+ 		}
+ 	}
+ 
+ 	elog(ERROR, "GenericXLogRegister: maximum number of %d buffers is exceeded", 
+ 		MAX_GENERIC_XLOG_PAGES);
+ 
+ 	/* keep compiler quiet */
+ 	return NULL;
+ }
+ 
+ void
+ GenericXLogMemmove(Pointer dst, Pointer src, OffsetNumber len)
+ {
+ 	Page page;
+ 	int i, block_id = -1;
+ 
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		elog(ERROR, "GenericXLogMemmove: generic xlog isn't started");
+ 
+ 	/* Find block */
+ 	for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 	{
+ 		page = pages[i].image;
+ 
+ 		if (dst >= page && dst < page + BLCKSZ)
+ 		{
+ 			block_id = i;
+ 			break;
+ 		}
+ 	}
+ 
+ 	/* Check block is found */
+ 	if (block_id < 0)
+ 		elog(ERROR, "GenericXLogMemmove: page not found");
+ 
+ 	/* Check both source and destination of memmove are inside of page */
+ 	if (src < page || src + len > page + BLCKSZ)
+ 		elog(ERROR, "GenericXLogMemmove: source is outside of page");
+ 	if (dst < page || dst + len > page + BLCKSZ)
+ 		elog(ERROR, "GenericXLogMemmove: desitnation is outside of page");
+ 
+ 	if (pages[block_id].fullImage)
+ 		return;
+ 
+ 	memoryMove(&pages[block_id], dst - page, src - page, len);
+ 	memmove(dst, src, len);
+ }
+ 
+ #ifdef DEBUG_PRINT
+ static void
+ printDataBlock(Pointer ptr, Pointer end)
+ {
+ 	OffsetNumber length, offset = 0, source;
+ 
+ 	while (ptr < end)
+ 	{
+ 		memcpy(&length, ptr, sizeof(length));
+ 		ptr += sizeof(length);
+ 
+ 		if (length & MOVE_FLAG)
+ 		{
+ 			length &= LENGTH_MASK;
+ 			memcpy(&source, ptr, sizeof(source));
+ 			ptr += sizeof(source);
+ 			printf("\t%d: move %d from %d\n", offset, length, source);
+ 		}
+ 		else
+ 		{
+ 			OffsetNumber i;
+ 			printf("\t%d: set %d -", offset, length);
+ 			for (i = 0; i < length; i++)
+ 			{
+ 				printf(" %02X", (uint8)ptr[i]);
+ 			}
+ 			printf("\n");
+ 			ptr += length;
+ 		}
+ 		offset += length;
+ 	}
+ }
+ #endif
+ 
+ XLogRecPtr
+ GenericXLogFinish(void)
+ {
+ 	XLogRecPtr lsn = InvalidXLogRecPtr;
+ 	int i;
+ 
+ 	if (genericXlogStatus == GXLOG_LOGGED)
+ 	{
+ 		START_CRIT_SECTION();
+ 		XLogBeginInsert();
+ 
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			char	tmp[BLCKSZ];
+ 
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 
+ 			memcpy(tmp, pages[i].image, BLCKSZ);
+ 			memcpy(pages[i].image, BufferGetPage(pages[i].buffer), BLCKSZ);
+ 			memcpy(BufferGetPage(pages[i].buffer), tmp, BLCKSZ);
+ 
+ 			if (pages[i].fullImage)
+ 			{
+ 				XLogRegisterBuffer(i, pages[i].buffer, REGBUF_FORCE_IMAGE);
+ 			}
+ 			else
+ 			{
+ 				XLogRegisterBuffer(i, pages[i].buffer, REGBUF_STANDARD);
+ 				writeDifferentialData(&pages[i]);
+ 				XLogRegisterBufData(i, pages[i].data, pages[i].dataLen);
+ 			}
+ 		}
+ 
+ 		lsn = XLogInsert(RM_GENERIC_ID, 0);
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 			PageSetLSN(BufferGetPage(pages[i].buffer), lsn);
+ 			MarkBufferDirty(pages[i].buffer);
+ 		}
+ 		END_CRIT_SECTION();
+ 	}
+ 	else if (genericXlogStatus == GXLOG_UNLOGGED)
+ 	{
+ 		START_CRIT_SECTION();
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 			memcpy(BufferGetPage(pages[i].buffer), pages[i].image, BLCKSZ);
+ 			MarkBufferDirty(pages[i].buffer);
+ 		}
+ 		END_CRIT_SECTION();
+ 	}
+ 	else
+ 	{
+ 		elog(ERROR, "GenericXLogFinish: generic xlog isn't started");
+ 	}
+ 
+ #ifdef DEBUG_PRINT
+ 	for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 	{
+ 		if (BufferIsInvalid(pages[i].buffer))
+ 			continue;
+ 		printf("Block %08X\n", BufferGetBlockNumber(pages[i].buffer));
+ 		if (pages[i].fullImage)
+ 			printf("full image is forced");
+ 		else
+ 			printDataBlock(pages[i].data, pages[i].data + pages[i].dataLen);
+ 	}
+ #endif
+ 
+ 	genericXlogStatus = GXLOG_NOT_STARTED;
+ 
+ 	return lsn;
+ }
+ 
+ void
+ GenericXLogAbort(void)
+ {
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		elog(ERROR, "GenericXLogMemmove: generic xlog isn't started");
+ 
+ 	genericXlogStatus = GXLOG_NOT_STARTED;
+ }
+ 
+ static void
+ applyPageRedo(Page page, Page image, Pointer data, Size dataSize)
+ {
+ 	OffsetNumber length, offset = 0, source;
+ 	Pointer ptr = data, end = data + dataSize;
+ 
+ 	while (ptr < end)
+ 	{
+ 		memcpy(&length, ptr, sizeof(length));
+ 		ptr += sizeof(length);
+ 
+ 		if (length & MOVE_FLAG)
+ 		{
+ 			length &= LENGTH_MASK;
+ 			memcpy(&source, ptr, sizeof(source));
+ 			ptr += sizeof(source);
+ 
+ 			memcpy(page + offset, image + source, length);
+ 		}
+ 		else
+ 		{
+ 			memcpy(page + offset, ptr, length);
+ 
+ 			ptr += length;
+ 		}
+ 		offset += length;
+ 	}
+ }
+ 
+ void
+ generic_redo(XLogReaderState *record)
+ {
+ 	uint8		block_id;
+ 	Buffer		buffers[MAX_GENERIC_XLOG_PAGES] = {InvalidBuffer};
+ 	XLogRecPtr	lsn = record->EndRecPtr;
+ 	char		image[BLCKSZ];
+ 
+ 	Assert(record->max_block_id < MAX_GENERIC_XLOG_PAGES);
+ 
+ #ifdef DEBUG_PRINT
+ 	elog(LOG, "Generic redo: %d (pid = %d)!", record->max_block_id, MyProcPid);
+ #endif
+ 
+ 	for (block_id = 0; block_id <= record->max_block_id; block_id++)
+ 	{
+ 		XLogRedoAction action;
+ 
+ 		if (!XLogRecHasBlockRef(record, block_id))
+ 			continue;
+ 		action = XLogReadBufferForRedo(record, block_id, &buffers[block_id]);
+ #ifdef DEBUG_PRINT
+ 		elog(LOG, "%d - %d", block_id, action);
+ #endif
+ 		if (action == BLK_NEEDS_REDO)
+ 		{
+ 			Pointer	blockData;
+ 			Size	blockDataSize;
+ 			Page	page;
+ 
+ 			page = BufferGetPage(buffers[block_id]);
+ 			memcpy(image, page, BLCKSZ);
+ 
+ 			blockData = XLogRecGetBlockData(record, block_id, &blockDataSize);
+ 
+ #ifdef DEBUG_PRINT
+ 			printDataBlock(blockData, blockData + blockDataSize);
+ #endif
+ 
+ 			applyPageRedo(page, image, blockData, blockDataSize);
+ 
+ 			PageSetLSN(page, lsn);
+ 			MarkBufferDirty(buffers[block_id]);
+ 		}
+ #ifdef DEBUG_PRINT
+ 		if (action == BLK_RESTORED)
+ 		{
+ 			int i;
+ 			RestoreBlockImage(record, block_id, image);
+ 			printf("Restored, image: ");
+ 			for (i = 0; i < BLCKSZ; i++)
+ 			{
+ 				printf("%02X ", (uint8)image[i]);
+ 			}
+ 			printf("\n");
+ 		}
+ #endif
+ 	}
+ 
+ 	for (block_id = 0; block_id <= record->max_block_id; block_id++)
+ 	{
+ 		if (BufferIsValid(buffers[block_id]))
+ 			UnlockReleaseBuffer(buffers[block_id]);
+ 	}
+ }
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
new file mode 100644
index 7c4d773..7b38c16
*** a/src/backend/access/transam/rmgr.c
--- b/src/backend/access/transam/rmgr.c
***************
*** 11,16 ****
--- 11,17 ----
  #include "access/commit_ts.h"
  #include "access/gin.h"
  #include "access/gist_private.h"
+ #include "access/generic_xlog.h"
  #include "access/hash.h"
  #include "access/heapam_xlog.h"
  #include "access/brin_xlog.h"
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
new file mode 100644
index 88c3a49..2d69dc2
*** a/src/backend/replication/logical/decode.c
--- b/src/backend/replication/logical/decode.c
*************** LogicalDecodingProcessRecord(LogicalDeco
*** 135,140 ****
--- 135,141 ----
  		case RM_BRIN_ID:
  		case RM_COMMIT_TS_ID:
  		case RM_REPLORIGIN_ID:
+ 		case RM_GENERIC_ID:
  			break;
  		case RM_NEXT_ID:
  			elog(ERROR, "unexpected RM_NEXT_ID rmgr_id: %u", (RmgrIds) XLogRecGetRmid(buf.record));
diff --git a/src/bin/pg_xlogdump/.gitignore b/src/bin/pg_xlogdump/.gitignore
new file mode 100644
index eebaf30..33a1acf
*** a/src/bin/pg_xlogdump/.gitignore
--- b/src/bin/pg_xlogdump/.gitignore
***************
*** 4,9 ****
--- 4,10 ----
  /clogdesc.c
  /committsdesc.c
  /dbasedesc.c
+ /genericdesc.c
  /gindesc.c
  /gistdesc.c
  /hashdesc.c
diff --git a/src/bin/pg_xlogdump/rmgrdesc.c b/src/bin/pg_xlogdump/rmgrdesc.c
new file mode 100644
index f9cd395..cff7e59
*** a/src/bin/pg_xlogdump/rmgrdesc.c
--- b/src/bin/pg_xlogdump/rmgrdesc.c
***************
*** 11,16 ****
--- 11,17 ----
  #include "access/brin_xlog.h"
  #include "access/clog.h"
  #include "access/commit_ts.h"
+ #include "access/generic_xlog.h"
  #include "access/gin.h"
  #include "access/gist_private.h"
  #include "access/hash.h"
diff --git a/src/include/access/generic_xlog.h b/src/include/access/generic_xlog.h
new file mode 100644
index ...45c593d
*** a/src/include/access/generic_xlog.h
--- b/src/include/access/generic_xlog.h
***************
*** 0 ****
--- 1,32 ----
+ /*-------------------------------------------------------------------------
+  *
+  * generic_xlog.h
+  *	  POSTGRES generic XLOG definitions.
+  *
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/access/generic_xlog.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef GENERIC_XLOG_H
+ #define GENERIC_XLOG_H
+ 
+ #include "access/xlog.h"
+ #include "access/xlog_internal.h"
+ #include "storage/bufpage.h"
+ #include "utils/rel.h"
+ 
+ extern void GenericXLogStart(Relation index);
+ extern Page GenericXLogRegister(Buffer buffer, bool isNew);
+ extern void GenericXLogMemmove(Pointer dst, Pointer src, OffsetNumber len);
+ extern XLogRecPtr GenericXLogFinish(void);
+ extern void GenericXLogAbort(void);
+ 
+ extern void generic_redo(XLogReaderState *record);
+ extern const char *generic_identify(uint8 info);
+ extern void generic_desc(StringInfo buf, XLogReaderState *record);
+ 
+ #endif   /* GENERIC_XLOG_H */
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
new file mode 100644
index fab912d..3cfe6f7
*** a/src/include/access/rmgrlist.h
--- b/src/include/access/rmgrlist.h
*************** PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo
*** 45,47 ****
--- 45,48 ----
  PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL)
  PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL)
  PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL)
+ PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL)
bloom-contrib.5.patchapplication/octet-stream; name=bloom-contrib.5.patchDownload
diff --git a/contrib/Makefile b/contrib/Makefile
new file mode 100644
index bd251f6..3ac3818
*** a/contrib/Makefile
--- b/contrib/Makefile
*************** SUBDIRS = \
*** 8,13 ****
--- 8,14 ----
  		adminpack	\
  		auth_delay	\
  		auto_explain	\
+ 		bloom		\
  		btree_gin	\
  		btree_gist	\
  		chkpass		\
diff --git a/contrib/bloom/.gitignore b/contrib/bloom/.gitignore
new file mode 100644
index ...5dcb3ff
*** a/contrib/bloom/.gitignore
--- b/contrib/bloom/.gitignore
***************
*** 0 ****
--- 1,4 ----
+ # Generated subdirectories
+ /log/
+ /results/
+ /tmp_check/
diff --git a/contrib/bloom/Makefile b/contrib/bloom/Makefile
new file mode 100644
index ...f540027
*** a/contrib/bloom/Makefile
--- b/contrib/bloom/Makefile
***************
*** 0 ****
--- 1,21 ----
+ # contrib/bloom/Makefile
+ 
+ MODULE_big = bloom
+ OBJS = blcost.o blinsert.o blscan.o blutils.o blvacuum.o blvalidate.o $(WIN32RES)
+ 
+ EXTENSION = bloom
+ DATA = bloom--1.0.sql
+ PGFILEDESC = "bloom access method - signature file based index"
+ 
+ REGRESS = bloom
+ 
+ ifdef USE_PGXS
+ PG_CONFIG = pg_config
+ PGXS := $(shell $(PG_CONFIG) --pgxs)
+ include $(PGXS)
+ else
+ subdir = contrib/bloom
+ top_builddir = ../..
+ include $(top_builddir)/src/Makefile.global
+ include $(top_srcdir)/contrib/contrib-global.mk
+ endif
diff --git a/contrib/bloom/README b/contrib/bloom/README
new file mode 100644
index ...205b711
*** a/contrib/bloom/README
--- b/contrib/bloom/README
***************
*** 0 ****
--- 1,91 ----
+ contrib/bloom provides signature file based index.
+ 
+ Authors: Teodor Sigaev (teodor@sigaev.ru), Oleg Bartunov (oleg@sai.msu.su),
+          Alexander Korotkov (aekorotkov@gmail.com)
+ 
+ 
+ This index is useful if table has many attributes and queries can include
+ their arbitary combinations. Traditional Btree index is faster than
+ bloom index , but it'd require too many indexes to support all possible 
+ queries, while one need only one bloom index. Bloom index supports only 
+ equality comparison. Since it's a signature file, not a tree, it always
+ should be readed fully, but sequentially, so search performance is 
+ constant and doesn't depends on a query. 
+ Implementation of Bloom filter (http://en.wikipedia.org/wiki/Bloom_filter)
+ allows fast exclusion of non-candidate tuples.
+ Since signature is a lossy representation of all indexed attributes, 
+ search results should be rechecked using heap information. 
+ User can specify signature length (in uint16, default is 5) and the number of 
+ bits, which can be setted, per attribute ( 1 < colN < 2048 ).
+ 
+  For example:
+ 
+ CREATE INDEX bloomidx ON tbloom(i1,i2,i3) 
+        WITH (length=5, col1=2, col2=2, col3=4);
+ 
+ Here, we create bloom index with signature length 80 bits and attributes
+ i1, i2  mapped to 2 bits, attribute i3 - to 4 bits.
+ 
+ 
+ Todo: 
+ * add more opclasses
+ * better configurability
+ * add support of arrays with contains and intersection operations
+ 
+ Example of usage:
+ 
+ select
+         (generate_series(1,1000)*random())::int as i1,
+         (generate_series(1,1000)*random())::int as i2,
+         (generate_series(1,1000)*random())::int as i3,
+         (generate_series(1,1000)*random())::int as i4,
+         (generate_series(1,1000)*random())::int as i5,
+         (generate_series(1,1000)*random())::int as i6,
+         (generate_series(1,1000)*random())::int as i7,
+         (generate_series(1,1000)*random())::int as i8,
+         (generate_series(1,1000)*random())::int as i9,
+         (generate_series(1,1000)*random())::int as i10,
+         (generate_series(1,1000)*random())::int as i11,
+         (generate_series(1,1000)*random())::int as i12,
+         (generate_series(1,1000)*random())::int as i13
+ into tbloom;
+ create index bloomidx on tbloom using
+ bloom(i1,i2,i3,i4,i5,i6,i7,i8,i9,i10,i11,i12);
+ select pg_relation_size('bloomidx');
+ create index btree_idx on tbloom(i1,i2,i3,i4,i5,i6,i7,i8,i9,i10,i11,i12);
+ select pg_relation_size('btree_idx');
+ 
+ 
+ =# explain analyze select * from tbloom where i2=20 and i10=15;
+                                                    QUERY PLAN
+ -----------------------------------------------------------------------------------------------------------------
+  Bitmap Heap Scan on tbloom  (cost=1.50..5.52 rows=1 width=52) (actual time=0.057..0.057 rows=0 loops=1)
+    Recheck Cond: ((i2 = 20) AND (i10 = 15))
+    ->  Bitmap Index Scan on bloomidx  (cost=0.00..1.50 rows=1 width=0) (actual time=0.041..0.041 rows=9 loops=1)
+          Index Cond: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.081 ms
+ (5 rows)
+ 
+ Seqscan is slow.
+ 
+ =# set enable_bitmapscan = off;
+ =# set enable_indexscan = off;
+ =# explain analyze select * from tbloom where i2=20 and i10=15;
+                                             QUERY PLAN
+ --------------------------------------------------------------------------------------------------
+  Seq Scan on tbloom  (cost=0.00..25.00 rows=1 width=52) (actual time=0.162..0.162 rows=0 loops=1)
+    Filter: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.181 ms
+ (3 rows)
+ 
+ Btree index will be not used.
+ 
+ =# drop index bloomidx;
+ =# create index btree_idx on tbloom(i1,i2,i3,i4,i5,i6,i7,i8,i9,i10,i11,i12);
+ =# explain analyze  select * from tbloom where i2=20 and i10=15;
+                                             QUERY PLAN
+ --------------------------------------------------------------------------------------------------
+  Seq Scan on tbloom (cost=0.00..25.00 rows=1 width=52) (actual time=0.210..0.210 rows=0 loops=1)
+    Filter: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.250 ms
+ (3 rows)
diff --git a/contrib/bloom/blcost.c b/contrib/bloom/blcost.c
new file mode 100644
index ...1265527
*** a/contrib/bloom/blcost.c
--- b/contrib/bloom/blcost.c
***************
*** 0 ****
--- 1,45 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blcost.c
+  *		Cost estimate function for bloom indexes.
+  *
+  * Copyright (c) 2015, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blcost.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "fmgr.h"
+ #include "optimizer/cost.h"
+ #include "utils/selfuncs.h"
+ 
+ #include "bloom.h"
+ 
+ void
+ blcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
+ 			   Cost *indexStartupCost, Cost *indexTotalCost,
+ 			   Selectivity *indexSelectivity, double *indexCorrelation)
+ {
+ 	IndexOptInfo   *index = path->indexinfo;
+ 	List		   *qinfos;
+ 	GenericCosts	costs;
+ 
+ 	/* Do preliminary analysis of indexquals */
+ 	qinfos = deconstruct_indexquals(path);
+ 
+ 	MemSet(&costs, 0, sizeof(costs));
+ 
+ 	/* We have to visit all index tuples anyway */
+ 	costs.numIndexTuples = index->tuples;
+ 
+ 	/* Use generic estimate */
+ 	genericcostestimate(root, path, loop_count, qinfos, &costs);
+ 
+ 	*indexStartupCost = costs.indexStartupCost;
+ 	*indexTotalCost = costs.indexTotalCost;
+ 	*indexSelectivity = costs.indexSelectivity;
+ 	*indexCorrelation = costs.indexCorrelation;
+ }
diff --git a/contrib/bloom/blinsert.c b/contrib/bloom/blinsert.c
new file mode 100644
index ...d90515f
*** a/contrib/bloom/blinsert.c
--- b/contrib/bloom/blinsert.c
***************
*** 0 ****
--- 1,276 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blinsert.c
+  *		Bloom index build and insert functions.
+  *
+  * Copyright (c) 2015, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blinsert.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "access/generic_xlog.h"
+ #include "catalog/index.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ 
+ #include "bloom.h"
+ 
+ PG_MODULE_MAGIC;
+ 
+ typedef struct
+ {
+ 	BloomState		blstate;
+ 	MemoryContext	tmpCtx;
+ 	char			data[BLCKSZ];
+ 	int64			count;
+ } BloomBuildState;
+ 
+ static void
+ flushBuildBuffer(Relation index, BloomBuildState *buildstate)
+ {
+ 	Page	page;
+ 	Buffer	buffer = BloomNewBuffer(index);
+ 
+ 	GenericXLogStart(index);
+ 	page = GenericXLogRegister(buffer, true);
+ 	memcpy(page, buildstate->data, BLCKSZ);
+ 	GenericXLogFinish();
+ 	UnlockReleaseBuffer(buffer);
+ }
+ 
+ static void
+ bloomBuildCallback(Relation index, HeapTuple htup, Datum *values,
+ 					bool *isnull, bool tupleIsAlive, void *state)
+ {
+ 	BloomBuildState	*buildstate = (BloomBuildState*)state;
+ 	MemoryContext	oldCtx;
+ 	BloomTuple		*itup;
+ 
+ 	oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
+ 
+ 	itup = BloomFormTuple(&buildstate->blstate, &htup->t_self, values, isnull);
+ 
+ 	if (BloomPageAddItem(&buildstate->blstate, buildstate->data, itup) == false)
+ 	{
+ 		flushBuildBuffer(index, buildstate);
+ 
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		memset(buildstate->data, 0, BLCKSZ);
+ 		BloomInitPage(buildstate->data, 0);
+ 		buildstate->count = 0;
+ 
+ 		if (BloomPageAddItem(&buildstate->blstate, buildstate->data, itup) == false)
+ 			elog(ERROR, "can not add new tuple"); /* should not be here! */
+ 	}
+ 	else
+ 	{
+ 		buildstate->count++;
+ 	}
+ 
+ 	MemoryContextSwitchTo(oldCtx);
+ 	MemoryContextReset(buildstate->tmpCtx);
+ }
+ 
+ IndexBuildResult *
+ blbuild(Relation heap, Relation index, IndexInfo *indexInfo)
+ {
+ 	IndexBuildResult   *result;
+ 	double				reltuples;
+ 	BloomBuildState		buildstate;
+ 	Buffer				metaBuffer;
+ 	Page				metaPage;
+ 
+ 	if (RelationGetNumberOfBlocks(index) != 0)
+ 		elog(ERROR, "index \"%s\" already contains data",
+ 			RelationGetRelationName(index));
+ 
+ 	/* initialize the meta page */
+ 	metaBuffer = BloomNewBuffer(index);
+ 	GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(metaBuffer, true);
+ 	BloomInitMetapage(metaPage, index);
+ 	GenericXLogFinish();
+ 	UnlockReleaseBuffer(metaBuffer);
+ 
+ 	initBloomState(&buildstate.blstate, index);
+ 
+ 	buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
+ 												"Bloom build temporary context",
+ 												ALLOCSET_DEFAULT_MINSIZE,
+ 												ALLOCSET_DEFAULT_INITSIZE,
+ 												ALLOCSET_DEFAULT_MAXSIZE);
+ 
+ 	memset(buildstate.data, 0, BLCKSZ);
+ 	BloomInitPage(buildstate.data, 0);
+ 	buildstate.count = 0;
+ 
+ 	reltuples = IndexBuildHeapScan(heap, index, indexInfo, true,
+ 									bloomBuildCallback, (void *) &buildstate);
+ 
+ 	if (buildstate.count > 0)
+ 		flushBuildBuffer(index, &buildstate);
+ 
+ 	MemoryContextDelete(buildstate.tmpCtx);
+ 
+ 	result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
+ 	result->heap_tuples = result->index_tuples = reltuples;
+ 
+ 	return result;
+ }
+ 
+ void
+ blbuildempty(Relation index)
+ {
+ 	Buffer		metaBuffer;
+ 	Page		metaPage;
+ 
+ 	if (RelationGetNumberOfBlocks(index) != 0)
+ 		elog(ERROR, "index \"%s\" already contains data",
+ 			RelationGetRelationName(index));
+ 
+ 	/* initialize the meta page */
+ 	metaBuffer = BloomNewBuffer(index);
+ 
+ 	GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(metaBuffer, true);
+ 	BloomInitMetapage(metaPage, index);
+ 	GenericXLogFinish();
+ 
+ 	UnlockReleaseBuffer(metaBuffer);
+ }
+ 
+ bool
+ blinsert(Relation index, Datum *values, bool *isnull,
+ 		ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique)
+ {
+ 	BloomState			blstate;
+ 	BloomTuple		   *itup;
+ 	MemoryContext		oldCtx;
+ 	MemoryContext		insertCtx;
+ 	BloomMetaPageData  *metaData;
+ 	Buffer				buffer,
+ 						metaBuffer;
+ 	Page				page,
+ 						metaPage;
+ 	BlockNumber			blkno = InvalidBlockNumber;
+ 	OffsetNumber		nStart;
+ 
+ 	insertCtx = AllocSetContextCreate(CurrentMemoryContext,
+ 										"Bloom insert temporary context",
+ 										ALLOCSET_DEFAULT_MINSIZE,
+ 										ALLOCSET_DEFAULT_INITSIZE,
+ 										ALLOCSET_DEFAULT_MAXSIZE);
+ 
+ 	oldCtx = MemoryContextSwitchTo(insertCtx);
+ 
+ 	initBloomState(&blstate, index);
+ 	itup = BloomFormTuple(&blstate, ht_ctid, values, isnull);
+ 
+ 	metaBuffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 	LockBuffer(metaBuffer, BUFFER_LOCK_SHARE);
+ 	metaData = BloomPageGetMeta(BufferGetPage(metaBuffer));
+ 
+ 	if (metaData->nEnd > metaData->nStart)
+ 	{
+ 		Page	page;
+ 
+ 		blkno = metaData->notFullPage[ metaData->nStart ];
+ 
+ 		Assert(blkno != InvalidBlockNumber);
+ 		LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		GenericXLogStart(index);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		if (BloomPageAddItem(&blstate, page, itup))
+ 		{
+ 			GenericXLogFinish();
+ 			UnlockReleaseBuffer(buffer);
+ 			ReleaseBuffer(metaBuffer);
+ 			goto away;
+ 		}
+ 		else
+ 		{
+ 			GenericXLogAbort();
+ 			UnlockReleaseBuffer(buffer);
+ 		}
+ 	}
+ 	else
+ 	{
+ 		/* no avaliable pages */
+ 		LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
+ 	}
+ 
+ 	/* protect any changes on metapage with a help of CRIT_SECTION */
+ 
+ 	LockBuffer(metaBuffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 	GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(metaBuffer, false);
+ 	metaData = BloomPageGetMeta(metaPage);
+ 
+ 	nStart = metaData->nStart;
+ 	if (metaData->nEnd >nStart &&
+ 		blkno == metaData->notFullPage[ nStart ] )
+ 		nStart++;
+ 
+ 	while (metaData->nEnd > nStart)
+ 	{
+ 		blkno = metaData->notFullPage[ nStart ];
+ 		Assert(blkno != InvalidBlockNumber);
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		if (BloomPageAddItem(&blstate, page, itup))
+ 		{
+ 			GenericXLogFinish();
+ 			UnlockReleaseBuffer(buffer);
+ 			UnlockReleaseBuffer(metaBuffer);
+ 			goto away;
+ 		}
+ 		else
+ 		{
+ 			UnlockReleaseBuffer(buffer);
+ 		}
+ 		nStart++;
+ 	}
+ 
+ 	GenericXLogAbort();
+ 
+ 	buffer = BloomNewBuffer(index);
+ 
+ 	GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(metaBuffer, false);
+ 	metaData = BloomPageGetMeta(metaPage);
+ 	page = GenericXLogRegister(buffer, true);
+ 	BloomInitPage(page, 0);
+ 	BloomPageAddItem(&blstate, page, itup);
+ 
+ 	metaData->nStart = 0;
+ 	metaData->nEnd = 1;
+ 	metaData->notFullPage[ 0 ] = BufferGetBlockNumber(buffer);
+ 
+ 	GenericXLogFinish();
+ 
+ 	UnlockReleaseBuffer(buffer);
+ 	UnlockReleaseBuffer(metaBuffer);
+ 
+ away:
+ 	MemoryContextSwitchTo(oldCtx);
+ 	MemoryContextDelete(insertCtx);
+ 
+ 	return false;
+ }
diff --git a/contrib/bloom/bloom--1.0.sql b/contrib/bloom/bloom--1.0.sql
new file mode 100644
index ...11a181c
*** a/contrib/bloom/bloom--1.0.sql
--- b/contrib/bloom/bloom--1.0.sql
***************
*** 0 ****
--- 1,21 ----
+ CREATE OR REPLACE FUNCTION blhandler(internal)
+ RETURNS index_am_handler
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C;
+ 
+ -- Access method
+ CREATE ACCESS METHOD bloom HANDLER blhandler;
+ 
+ -- Opclasses
+ 
+ CREATE OPERATOR CLASS int4_ops 
+ DEFAULT FOR TYPE int4 USING bloom AS
+ 	OPERATOR	1	=(int4, int4),
+ 	FUNCTION	1	hashint4(int4);
+ 
+ CREATE OPERATOR CLASS text_ops 
+ DEFAULT FOR TYPE text USING bloom AS
+ 	OPERATOR	1	=(text, text),
+ 	FUNCTION	1	hashtext(text);
+ 
+ 
diff --git a/contrib/bloom/bloom.control b/contrib/bloom/bloom.control
new file mode 100644
index ...4d4124b
*** a/contrib/bloom/bloom.control
--- b/contrib/bloom/bloom.control
***************
*** 0 ****
--- 1,5 ----
+ # bloom extension
+ comment = 'bloom access method - signature file based index'
+ default_version = '1.0'
+ module_pathname = '$libdir/bloom'
+ relocatable = true
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
new file mode 100644
index ...c4df664
*** a/contrib/bloom/bloom.h
--- b/contrib/bloom/bloom.h
***************
*** 0 ****
--- 1,161 ----
+ #ifndef _BLOOM_H_
+ #define _BLOOM_H_
+ 
+ #include "access/amapi.h"
+ #include "access/generic_xlog.h"
+ #include "access/itup.h"
+ #include "access/xlog.h"
+ #include "nodes/relation.h"
+ #include "fmgr.h"
+ 
+ #define	BLOOM_HASH_PROC			1
+ #define	BLOOM_NPROC				1
+ 
+ #define	BLOOM_EQUAL_STRATEGY	1
+ #define	BLOOM_NSTRATEGIES		1
+ 
+ typedef struct BloomPageOpaqueData
+ {
+ 	OffsetNumber	maxoff;
+ 	uint16			flags;
+ } BloomPageOpaqueData;
+ 
+ typedef BloomPageOpaqueData *BloomPageOpaque;
+ 
+ #define BLOOM_META		(1<<0)
+ #define BLOOM_DELETED	(2<<0)
+ 
+ #define BloomPageGetOpaque(page) ((BloomPageOpaque) PageGetSpecialPointer(page))
+ #define BloomPageGetMaxOffset(page) (BloomPageGetOpaque(page)->maxoff)
+ #define BloomPageIsMeta(page) (BloomPageGetOpaque(page)->flags & BLOOM_META)
+ #define BloomPageIsDeleted(page) (BloomPageGetOpaque(page)->flags & BLOOM_DELETED)
+ #define BloomPageSetDeleted(page) (BloomPageGetOpaque(page)->flags |= BLOOM_DELETED)
+ #define BloomPageSetNonDeleted(page) (BloomPageGetOpaque(page)->flags &= ~BLOOM_DELETED)
+ #define BloomPageGetData(page)		((BloomTuple *)PageGetContents(page))
+ #define BloomPageGetTuple(state, page, offset) \
+ 	((BloomTuple *)(PageGetContents(page) \
+ 		+ (state)->sizeOfBloomTuple * ((offset) - 1)))
+ #define BloomPageGetNextTuple(state, tuple) \
+ 	((BloomTuple *)((Pointer)(tuple) + (state)->sizeOfBloomTuple))
+ 
+ #define BLOOM_METAPAGE_BLKNO  	(0)
+ #define BLOOM_HEAD_BLKNO  		(1)
+ 
+ typedef struct BloomOptions 
+ {
+ 	int32	vl_len_;	/* varlena header (do not touch directly!) */
+ 	int		bloomLength;	
+ 	int		bitSize[INDEX_MAX_KEYS];
+ } BloomOptions;
+ 
+ 
+ 
+ typedef BlockNumber FreeBlockNumberArray[
+ 			MAXALIGN_DOWN(
+ 				BLCKSZ - 
+ 					SizeOfPageHeaderData - 
+ 					MAXALIGN(sizeof(BloomPageOpaqueData)) - 
+ 					/* header of BloomMetaPageData struct */
+ 					MAXALIGN(sizeof(uint16) * 2 + sizeof(uint32) + sizeof(BloomOptions)) 
+ 			) / sizeof(BlockNumber) 
+ 		];
+ 
+ typedef struct BloomMetaPageData
+ {
+ 	uint32					magickNumber;
+ 	uint16					nStart;
+ 	uint16					nEnd;
+ 	BloomOptions			opts;
+ 	FreeBlockNumberArray	notFullPage;
+ } BloomMetaPageData;
+ 
+ #define BLOOM_MAGICK_NUMBER	(0xDBAC0DED)
+ 
+ #define BloomMetaBlockN		(sizeof(FreeBlockNumberArray) / sizeof(BlockNumber))
+ #define BloomPageGetMeta(p) \
+ 	((BloomMetaPageData *) PageGetContents(p))
+ 
+ typedef struct BloomState 
+ {
+ 	FmgrInfo			hashFn[INDEX_MAX_KEYS];
+ 	BloomOptions		*opts; /* stored in rd_amcache and defined at creation time */
+ 	int32				nColumns;
+ 	/* 
+ 	 * sizeOfBloomTuple is index's specific, and it depends on
+ 	 * reloptions, so precompute it
+ 	 */
+ 	int32				sizeOfBloomTuple; 
+ } BloomState;
+ 
+ #define BloomPageGetFreeSpace(state, page) \
+ 	(BLCKSZ - MAXALIGN(SizeOfPageHeaderData) \
+ 		- BloomPageGetMaxOffset(page) * (state)->sizeOfBloomTuple \
+ 		- MAXALIGN(sizeof(BloomPageOpaqueData)))
+ 
+ /*
+  * Tuples are very different from all other relations
+  */
+ typedef uint16	SignType;
+ 
+ typedef struct BloomTuple {
+ 	ItemPointerData		heapPtr;
+ 	SignType			sign[1];
+ } BloomTuple;
+ #define BLOOMTUPLEHDRSZ	offsetof(BloomTuple, sign)
+ 
+ #define BITBYTE 	(8)
+ #define BITSIGNTYPE	(BITBYTE * sizeof(SignType))
+ #define GETWORD(x,i) ( *( (SignType*)(x) + (int)( (i) / BITSIGNTYPE ) ) )
+ #define GETBITBYTE(x,i) ( ((SignType)(x)) >> i & 0x01 )
+ #define CLRBIT(x,i)   GETWORD(x,i) &= ~( 0x01 << ( (i) % BITSIGNTYPE ) )
+ #define SETBIT(x,i)   GETWORD(x,i) |=  ( 0x01 << ( (i) % BITSIGNTYPE ) )
+ #define GETBIT(x,i) ( (GETWORD(x,i) >> ( (i) % BITSIGNTYPE )) & 0x01 )
+ 
+ /* Opaque data structure for bloom index scan */
+ typedef struct BloomScanOpaqueData
+ {
+ 	SignType   *sign;	/* Scan signature */
+ 	BloomState	state;
+ } BloomScanOpaqueData;
+ 
+ typedef BloomScanOpaqueData *BloomScanOpaque;
+ 
+ /* blutils.c */
+ extern Datum blhandler(PG_FUNCTION_ARGS);
+ extern void initBloomState(BloomState *state, Relation index);
+ extern void BloomInitMetapage(Page page, Relation index);
+ extern void BloomInitPage(Page page, uint16 flags);
+ extern Buffer BloomNewBuffer(Relation index);
+ extern void signValue(BloomState *state, SignType *sign, Datum value, int attno);
+ extern BloomTuple* BloomFormTuple(BloomState *state, ItemPointer iptr, Datum *values, bool *isnull);
+ extern bool BloomPageAddItem(BloomState *state, Page page, BloomTuple *tuple);
+ 
+ /* blvalidate.c */
+ extern bool blvalidate(Oid opclassoid);
+ 
+ /* interface functions */
+ extern bool blinsert(Relation index, Datum *values, bool *isnull,
+ 					 ItemPointer ht_ctid, Relation heapRel,
+ 					 IndexUniqueCheck checkUnique);
+ extern IndexScanDesc blbeginscan(Relation r, int nkeys, int norderbys);
+ extern int64 blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
+ extern void blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ 					 ScanKey orderbys, int norderbys);
+ extern void blendscan(IndexScanDesc scan);
+ extern void blmarkpos(IndexScanDesc scan);
+ extern void blrestrpos(IndexScanDesc scan);
+ extern IndexBuildResult *blbuild(Relation heap, Relation index,
+ 								   struct IndexInfo *indexInfo);
+ extern void blbuildempty(Relation index);
+ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
+ 	IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback,
+ 	void *callback_state);
+ extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
+ 	IndexBulkDeleteResult *stats);
+ extern bytea *bloptions(Datum reloptions, bool validate);
+ extern void blcostestimate(PlannerInfo *root, IndexPath *path,
+ 						   double loop_count, Cost *indexStartupCost,
+ 						   Cost *indexTotalCost, Selectivity *indexSelectivity,
+ 						   double *indexCorrelation);
+ 
+ #endif
diff --git a/contrib/bloom/blscan.c b/contrib/bloom/blscan.c
new file mode 100644
index ...1670008
*** a/contrib/bloom/blscan.c
--- b/contrib/bloom/blscan.c
***************
*** 0 ****
--- 1,175 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blscan.c
+  *		Bloom index scan functions.
+  *
+  * Copyright (c) 2015, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blscan.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/relscan.h"
+ #include "pgstat.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/lmgr.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ 
+ #include "bloom.h"
+ 
+ IndexScanDesc
+ blbeginscan(Relation r, int nkeys, int norderbys)
+ {
+ 	IndexScanDesc scan;
+ 
+ 	scan = RelationGetIndexScan(r, nkeys, norderbys);
+ 
+ 	return scan;
+ }
+ 
+ void
+ blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ 							 ScanKey orderbys, int norderbys)
+ {
+ 	BloomScanOpaque so;
+ 
+ 	so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so == NULL)
+ 	{
+ 		/* if called from blbeginscan */
+ 		so = (BloomScanOpaque) palloc(sizeof(BloomScanOpaqueData));
+ 		initBloomState(&so->state, scan->indexRelation);
+ 		scan->opaque = so;
+ 
+ 	}
+ 	else
+ 	{
+ 		if (so->sign)
+ 			pfree(so->sign);
+ 	}
+ 	so->sign = NULL;
+ 
+ 	if (scankey && scan->numberOfKeys > 0)
+ 	{
+ 		memmove(scan->keyData, scankey,
+ 				scan->numberOfKeys * sizeof(ScanKeyData));
+ 	}
+ }
+ 
+ void
+ blendscan(IndexScanDesc scan)
+ {
+ 	BloomScanOpaque so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so->sign)
+ 		pfree(so->sign);
+ 	so->sign = NULL;
+ }
+ 
+ void
+ blmarkpos(IndexScanDesc scan)
+ {
+ 	elog(ERROR, "Bloom does not support mark/restore");
+ }
+ 
+ void
+ blrestrpos(IndexScanDesc scan)
+ {
+ 	elog(ERROR, "Bloom does not support mark/restore");
+ }
+ 
+ int64
+ blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
+ {
+ 	int64					ntids = 0;
+ 	BlockNumber				blkno = BLOOM_HEAD_BLKNO,
+ 							npages;
+ 	int						i;
+ 	BufferAccessStrategy	bas;
+ 	BloomScanOpaque 		so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so->sign == NULL && scan->numberOfKeys > 0)
+ 	{
+ 		/* New search: have to calculate search signature */
+ 		ScanKey skey = scan->keyData;
+ 
+ 		so->sign = palloc0(sizeof(SignType) * so->state.opts->bloomLength); 
+ 		
+ 		for(i = 0; i < scan->numberOfKeys; i++)
+ 		{
+ 			/*
+ 			 * Assume, that Bloom-indexable operators are strict, so nothing could
+ 			 * be found
+ 			 */
+ 
+ 			if (skey->sk_flags & SK_ISNULL)
+ 			{
+ 				pfree(so->sign);
+ 				so->sign = NULL;
+ 				return 0;
+ 			}
+ 
+ 			/* Add next value to the signature */
+ 			signValue(&so->state, so->sign, skey->sk_argument,
+ 															skey->sk_attno - 1);
+ 
+ 			skey++;
+ 		}
+ 	}
+ 
+ 	/*
+ 	 * We're going to read the whole index. This is why we use appropriate
+ 	 * buffer access strategy.
+ 	 */
+ 	bas = GetAccessStrategy(BAS_BULKREAD);
+ 	npages = RelationGetNumberOfBlocks(scan->indexRelation);
+ 
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		Buffer 			buffer;
+ 		Page			page;
+ 
+ 		buffer = ReadBufferExtended(scan->indexRelation, MAIN_FORKNUM,
+ 									blkno, RBM_NORMAL, bas);
+ 
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 		page = BufferGetPage(buffer);
+ 
+ 		if (!BloomPageIsDeleted(page))
+ 		{
+ 			OffsetNumber offset, maxOffset = BloomPageGetMaxOffset(page);
+ 
+ 			for (offset = 1; offset <= maxOffset; offset++)
+ 			{
+ 				BloomTuple *itup = BloomPageGetTuple(&so->state, page, offset);
+ 				bool		res = true;
+ 
+ 				/* Check index signature with scan signature */
+ 				for (i = 0; res && i < so->state.opts->bloomLength; i++)
+ 				{
+ 					if ((itup->sign[i] & so->sign[i]) != so->sign[i])
+ 						res = false;
+ 				}
+ 
+ 				/* Add matching tuples to bitmap */
+ 				if (res)
+ 				{
+ 					tbm_add_tuples(tbm, &itup->heapPtr, 1, true);
+ 					ntids++;
+ 				}
+ 			}
+ 		}
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 		CHECK_FOR_INTERRUPTS();
+ 	}
+ 	FreeAccessStrategy(bas);
+ 
+ 	return ntids;
+ }
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
new file mode 100644
index ...352b352
*** a/contrib/bloom/blutils.c
--- b/contrib/bloom/blutils.c
***************
*** 0 ****
--- 1,363 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blscan.c
+  *		Bloom index scan functions.
+  *
+  * Copyright (c) 2015, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blscan.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/amapi.h"
+ #include "access/generic_xlog.h"
+ #include "catalog/index.h"
+ #include "storage/lmgr.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "utils/memutils.h"
+ #include "access/reloptions.h"
+ #include "storage/freespace.h"
+ #include "storage/indexfsm.h"
+ 
+ #include "bloom.h"
+ 
+ PG_FUNCTION_INFO_V1(blhandler);
+ 
+ /*
+  * Bloom handler function: return IndexAmRoutine with access method parameters
+  * and callbacks.
+  */
+ Datum
+ blhandler(PG_FUNCTION_ARGS)
+ {
+ 	IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
+ 
+ 	amroutine->amstrategies = 1;
+ 	amroutine->amsupport = 1;
+ 	amroutine->amcanorder = false;
+ 	amroutine->amcanorderbyop = false;
+ 	amroutine->amcanbackward = false;
+ 	amroutine->amcanunique = false;
+ 	amroutine->amcanmulticol = true;
+ 	amroutine->amoptionalkey = true;
+ 	amroutine->amsearcharray = false;
+ 	amroutine->amsearchnulls = false;
+ 	amroutine->amstorage = false;
+ 	amroutine->amclusterable = false;
+ 	amroutine->ampredlocks = false;
+ 	amroutine->amkeytype = 0;
+ 
+ 	amroutine->aminsert = blinsert;
+ 	amroutine->ambeginscan = blbeginscan;
+ 	amroutine->amgettuple = NULL;
+ 	amroutine->amgetbitmap = blgetbitmap;
+ 	amroutine->amrescan = blrescan;
+ 	amroutine->amendscan = blendscan;
+ 	amroutine->ammarkpos = blmarkpos;
+ 	amroutine->amrestrpos = blrestrpos;
+ 	amroutine->ambuild = blbuild;
+ 	amroutine->ambuildempty = blbuildempty;
+ 	amroutine->ambulkdelete = blbulkdelete;
+ 	amroutine->amvacuumcleanup = blvacuumcleanup;
+ 	amroutine->amcanreturn = NULL;
+ 	amroutine->amcostestimate = blcostestimate;
+ 	amroutine->amoptions = bloptions;
+ 	amroutine->amvalidate = blvalidate;
+ 
+ 	PG_RETURN_POINTER(amroutine);
+ }
+ 
+ void 
+ initBloomState(BloomState *state, Relation index)
+ {
+ 	int	i;
+ 
+ 	state->nColumns = index->rd_att->natts;
+ 
+ 	/* Initialize hash function for each attribute */
+ 	for (i = 0; i < index->rd_att->natts; i++)
+ 	{
+ 		fmgr_info_copy(&(state->hashFn[i]),
+ 						index_getprocinfo(index, i + 1, BLOOM_HASH_PROC),
+ 						CurrentMemoryContext);
+ 	}
+ 
+ 	/* Inititalize amcache if needed */
+ 	if (!index->rd_amcache)
+ 	{
+ 		Buffer				buffer;
+ 		Page				page;
+ 		BloomMetaPageData	*meta;
+ 		BloomOptions		*opts;
+ 		BloomPageOpaque		opaque;
+ 
+ 		opts = MemoryContextAlloc(index->rd_indexcxt, sizeof(BloomOptions));
+ 
+ 		buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 
+ 		page = BufferGetPage(buffer);
+ 		opaque = BloomPageGetOpaque(page);
+ 
+ 		if (!BloomPageIsMeta(page))
+ 			elog(ERROR, "Relation is not a bloom index");
+ 		meta = BloomPageGetMeta(BufferGetPage(buffer));
+ 
+ 		if (meta->magickNumber != BLOOM_MAGICK_NUMBER)
+ 			elog(ERROR, "Relation is not a bloom index");
+ 
+ 		*opts = meta->opts;
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 
+ 		index->rd_amcache = (void *)opts;
+ 	}
+ 
+ 	state->opts = (BloomOptions *)index->rd_amcache;
+ 	state->sizeOfBloomTuple = BLOOMTUPLEHDRSZ +
+ 									sizeof(SignType) * state->opts->bloomLength; 
+ }
+ 
+ /*
+  * Add bits of given value to the signature.
+  */
+ void
+ signValue(BloomState *state, SignType *sign, Datum value, int attno)
+ {
+ 	uint32		hashVal;
+ 	int 		nBit, j;
+ 
+ 	/*
+ 	 * init generator with "column's" number to get
+ 	 * "hashed" seed for new value. We don't want to map
+ 	 * the same numbers from different columns into the same bits!
+ 	 */
+ 	srand(attno);
+ 
+ 	/*
+ 	 * Init hash sequence to map our value into bits. the same values
+ 	 * in different columns will be mapped into different bits because
+ 	 * of step above
+ 	 */
+ 	hashVal = DatumGetInt32(FunctionCall1(&state->hashFn[attno], value));
+ 	srand(hashVal ^ rand());
+ 
+ 	for (j = 0; j < state->opts->bitSize[attno]; j++)
+ 	{
+ 		/* prevent mutiple evaluation */
+ 		nBit = rand() % (state->opts->bloomLength * BITSIGNTYPE); 
+ 		SETBIT(sign, nBit);
+ 	}
+ }
+ 
+ /*
+  * Make bloom tuple from values.
+  */
+ BloomTuple *
+ BloomFormTuple(BloomState *state, ItemPointer iptr, Datum *values, bool *isnull)
+ {
+ 	int 		i;
+ 	BloomTuple *res = (BloomTuple *)palloc0(state->sizeOfBloomTuple);
+ 
+ 	res->heapPtr = *iptr;
+ 
+     /*
+ 	 * Blooming
+ 	 */
+ 	for (i = 0; i < state->nColumns; i++)
+ 	{
+ 		/* skip nulls */
+ 		if (isnull[i])
+ 			continue;
+ 
+ 		signValue(state, res->sign, values[i], i);
+ 	}
+ 
+ 	return res;
+ }
+ 
+ /*
+  * Add new bloom tuple to the page.
+  */
+ bool
+ BloomPageAddItem(BloomState *state, Page page, BloomTuple *tuple)
+ {
+ 	BloomTuple		   *itup;
+ 	BloomPageOpaque		opaque;
+ 	Pointer				ptr;
+ 
+ 	if (BloomPageGetFreeSpace(state, page) < state->sizeOfBloomTuple)
+ 		return false;
+ 
+ 	/* Copy new tuple to the end of page */
+ 	opaque = BloomPageGetOpaque(page);
+ 	itup = BloomPageGetTuple(state, page, opaque->maxoff + 1);
+ 	memcpy((Pointer)itup, (Pointer)tuple, state->sizeOfBloomTuple);
+ 
+ 	/* Adjust maxoff and pd_lower */
+ 	opaque->maxoff++;
+ 	ptr = (Pointer)BloomPageGetTuple(state, page, opaque->maxoff + 1);
+ 	((PageHeader) page)->pd_lower = ptr - page;
+ 
+ 	return true;
+ }
+ 
+ /*
+  * Allocate a new page (either by recycling, or by extending the index file)
+  * The returned buffer is already pinned and exclusive-locked
+  * Caller is responsible for initializing the page by calling BloomInitBuffer
+  */
+ Buffer
+ BloomNewBuffer(Relation index)
+ {
+ 	Buffer      buffer;
+ 	bool        needLock;
+ 
+ 	/* First, try to get a page from FSM */
+ 	for (;;)
+ 	{
+ 		BlockNumber blkno = GetFreeIndexPage(index);
+ 
+ 		if (blkno == InvalidBlockNumber)
+ 			break;
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 
+ 		/*
+ 		 * We have to guard against the possibility that someone else already
+ 		 * recycled this page; the buffer may be locked if so.
+ 		 */
+ 		if (ConditionalLockBuffer(buffer))
+ 		{
+ 			Page        page = BufferGetPage(buffer);
+ 
+ 			if (PageIsNew(page))
+ 				return buffer;  /* OK to use, if never initialized */
+ 
+ 			if (BloomPageIsDeleted(page))
+ 				return buffer;  /* OK to use */
+ 
+ 			LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ 		}
+ 
+ 		/* Can't use it, so release buffer and try again */
+ 		ReleaseBuffer(buffer);
+ 	}
+ 
+ 	/* Must extend the file */
+ 	needLock = !RELATION_IS_LOCAL(index);
+ 	if (needLock)
+ 		LockRelationForExtension(index, ExclusiveLock);
+ 
+ 	buffer = ReadBuffer(index, P_NEW);
+ 	LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 	if (needLock)
+ 		UnlockRelationForExtension(index, ExclusiveLock);
+ 
+ 	return buffer;
+ }
+ 
+ void
+ BloomInitPage(Page page, uint16 flags)
+ {
+ 	BloomPageOpaque opaque;
+ 
+ 	PageInit(page, BLCKSZ, sizeof(BloomPageOpaqueData));
+ 
+ 	opaque = BloomPageGetOpaque(page);
+ 	memset(opaque, 0, sizeof(BloomPageOpaqueData));
+ 	opaque->maxoff = 0;
+ 	opaque->flags = flags;
+ }
+ 
+ static BloomOptions *
+ makeDefaultBloomOptions(BloomOptions *opts)
+ {
+ 	int i;
+ 
+ 	if (!opts)
+ 		opts = palloc0(sizeof(BloomOptions));
+ 
+ 	if (opts->bloomLength <= 0)
+ 		opts->bloomLength = 5;
+ 
+ 	for(i = 0; i < INDEX_MAX_KEYS; i++)
+ 		if (opts->bitSize[i] <= 0
+ 				|| opts->bitSize[i] >= opts->bloomLength * sizeof(SignType))
+ 			opts->bitSize[i] = 2;
+ 
+ 	return opts;
+ }
+ 
+ void
+ BloomInitMetapage(Page page, Relation index)
+ {
+ 	BloomMetaPageData	*metadata;
+ 
+ 	BloomInitPage(page, BLOOM_META);
+ 	metadata = BloomPageGetMeta(page);
+ 	memset(metadata, 0, sizeof(BloomMetaPageData));
+ 	metadata->magickNumber = BLOOM_MAGICK_NUMBER;
+ 	metadata->opts = *makeDefaultBloomOptions((BloomOptions*)index->rd_options);
+ 	((PageHeader) page)->pd_lower += sizeof(BloomMetaPageData);
+ }
+ 
+ static relopt_kind bloom_kind = 0;
+ 
+ void _PG_init(void);
+ void 
+ _PG_init(void)
+ {
+ 	int i;
+ 	char				buf[16];
+ 
+ 	bloom_kind = add_reloption_kind();
+ 
+ 	add_int_reloption(bloom_kind, "length", "Length of signature in uint16 type",
+ 						5, 1, 256);
+ 
+ 	for(i = 0; i < INDEX_MAX_KEYS;i ++)
+ 	{
+ 		snprintf(buf, 16, "col%d", i+1);
+ 		add_int_reloption(bloom_kind, buf, "Number of bits for corresponding column",
+ 								2, 1, 2048);
+ 	}
+ }
+ 
+ bytea *
+ bloptions(Datum reloptions, bool validate)
+ {
+ 	relopt_value	   *options;
+ 	int					numoptions;
+ 	BloomOptions	   *rdopts;
+ 	relopt_parse_elt	tab[INDEX_MAX_KEYS + 1];
+ 	int					i;
+ 	char				buf[16];
+ 
+ 	tab[0].optname = "length";
+ 	tab[0].opttype = RELOPT_TYPE_INT;
+ 	tab[0].offset = offsetof(BloomOptions, bloomLength);
+ 
+ 	for(i = 0; i < INDEX_MAX_KEYS; i++)
+ 	{
+ 		snprintf(buf, sizeof(buf), "col%d", i + 1);
+ 		tab[i + 1].optname = pstrdup(buf);
+ 		tab[i + 1].opttype = RELOPT_TYPE_INT;
+ 		tab[i + 1].offset = offsetof(BloomOptions, bitSize[i]);
+ 	}
+ 
+ 	options = parseRelOptions(reloptions, validate, bloom_kind, &numoptions);
+ 	rdopts = allocateReloptStruct(sizeof(BloomOptions), options, numoptions);
+ 	fillRelOptions((void *) rdopts, sizeof(BloomOptions), options, numoptions,
+ 						validate, tab, INDEX_MAX_KEYS + 1);
+ 		
+ 	rdopts = makeDefaultBloomOptions(rdopts);
+ 
+ 	return (bytea *)rdopts;
+ }
diff --git a/contrib/bloom/blvacuum.c b/contrib/bloom/blvacuum.c
new file mode 100644
index ...78a75e7
*** a/contrib/bloom/blvacuum.c
--- b/contrib/bloom/blvacuum.c
***************
*** 0 ****
--- 1,183 ----
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "catalog/storage.h"
+ #include "commands/vacuum.h"
+ #include "miscadmin.h"
+ #include "postmaster/autovacuum.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "storage/lmgr.h"
+ 
+ #include "bloom.h"
+ 
+ IndexBulkDeleteResult *
+ blbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
+ 			 IndexBulkDeleteCallback callback, void *callback_state)
+ {
+ 	Relation				index = info->index;
+ 	BlockNumber				blkno,
+ 							npages;
+ 	FreeBlockNumberArray	notFullPage;
+ 	int						countPage = 0;
+ 	BloomState				state;
+ 	Buffer					buffer;
+ 	Page					page;
+ 
+ 	if (stats == NULL)
+ 		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+ 
+ 	initBloomState(&state, index); 
+ 
+ 	/*
+ 	 * Interate over the pages. We don't care about concurrently added pages,
+ 	 * they can't contain tuples to delete.
+ 	 */
+ 	npages = RelationGetNumberOfBlocks(index);
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		BloomTuple *itup, *itupPtr, *itupEnd;
+ 
+ 		buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
+ 									RBM_NORMAL, info->strategy);
+ 
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		GenericXLogStart(index);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		if (BloomPageIsDeleted(page))
+ 		{
+ 			UnlockReleaseBuffer(buffer);
+ 			CHECK_FOR_INTERRUPTS();
+ 			continue;
+ 		}
+ 
+ 		/* Iterate over the tuples */
+ 		itup = BloomPageGetTuple(&state, page, 1);
+ 		itupPtr = BloomPageGetTuple(&state, page, 1);
+ 		itupEnd = BloomPageGetTuple(&state, page, BloomPageGetMaxOffset(page) + 1);
+ 		while (itup < itupEnd)
+ 		{
+ 			/* Do we have to delete this tuple? */
+ 			if (callback(&itup->heapPtr, callback_state))
+ 			{
+ 				stats->tuples_removed += 1;
+ 				BloomPageGetOpaque(page)->maxoff--;
+ 			} 
+ 			else 
+ 			{
+ 				if (itupPtr != itup)
+ 				{
+ 					/*
+ 					 * If we already delete something before, we have to move
+ 					 * this tuple backward.
+ 					 */
+ 					GenericXLogMemmove((Pointer)itupPtr, (Pointer)itup,
+ 									   state.sizeOfBloomTuple);
+ 				}
+ 				stats->num_index_tuples++;
+ 				itupPtr = BloomPageGetNextTuple(&state, itupPtr);
+ 			}
+ 
+ 			itup = BloomPageGetNextTuple(&state, itup);
+ 		}
+ 
+ 		/* Did we delete something? */
+ 		if (itupPtr != itup)
+ 		{
+ 			/* Is it empty page now? */
+ 			if (itupPtr == BloomPageGetData(page))
+ 				BloomPageSetDeleted(page);
+ 			/* Adjust pg_lower */
+ 			((PageHeader) page)->pd_lower = (Pointer)itupPtr - page;
+ 			/* Finish WAL-logging */
+ 			GenericXLogFinish();
+ 		}
+ 		else
+ 		{
+ 			/* Didn't change anything: abort WAL-logging */
+ 			GenericXLogAbort();
+ 		}
+ 
+ 		if (!BloomPageIsDeleted(page) && 
+ 				BloomPageGetFreeSpace(&state, page) > state.sizeOfBloomTuple && 
+ 				countPage < BloomMetaBlockN)
+ 			notFullPage[countPage++] = blkno;
+ 		UnlockReleaseBuffer(buffer);
+ 		CHECK_FOR_INTERRUPTS();
+ 	}
+ 
+ 	if (countPage > 0)
+ 	{
+ 		BloomMetaPageData	*metaData;
+ 
+ 		buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 		GenericXLogStart(index);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		metaData = BloomPageGetMeta(page);
+ 		memcpy(metaData->notFullPage, notFullPage, sizeof(FreeBlockNumberArray));
+ 		metaData->nStart=0;
+ 		metaData->nEnd = countPage;
+ 
+ 		GenericXLogFinish();
+ 		UnlockReleaseBuffer(buffer);
+ 	}
+ 
+ 	return stats;
+ }
+ 
+ IndexBulkDeleteResult *
+ blvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
+ {
+ 	Relation	index = info->index;
+ 	BlockNumber npages,
+ 				blkno;
+ 	BlockNumber	totFreePages;
+ 
+ 	if (info->analyze_only)
+ 		return stats;
+ 
+ 	if (stats == NULL)
+ 		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+ 
+ 	/*
+ 	 * Iterate over the pages: insert deleted pages into FSM and collect
+ 	 * statistics.
+ 	 */
+ 	npages = RelationGetNumberOfBlocks(index);
+ 	totFreePages = 0;
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		Buffer		buffer;
+ 		Page		page;
+ 
+ 		vacuum_delay_point();
+ 
+ 		buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
+ 									RBM_NORMAL, info->strategy);
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 		page = (Page) BufferGetPage(buffer);
+ 																						
+ 		if (BloomPageIsDeleted(page))
+ 		{
+ 			RecordFreeIndexPage(index, blkno);
+ 			totFreePages++;
+ 		}
+ 		else
+ 		{
+ 			stats->num_index_tuples += BloomPageGetMaxOffset(page);
+ 			stats->estimated_count += BloomPageGetMaxOffset(page);
+ 		}
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 	}
+ 
+ 	IndexFreeSpaceMapVacuum(info->index);
+ 	stats->pages_free = totFreePages;
+ 	stats->num_pages = RelationGetNumberOfBlocks(index);
+ 
+ 	return stats;
+ }
diff --git a/contrib/bloom/blvalidate.c b/contrib/bloom/blvalidate.c
new file mode 100644
index ...c364c14
*** a/contrib/bloom/blvalidate.c
--- b/contrib/bloom/blvalidate.c
***************
*** 0 ****
--- 1,220 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blvalidate.c
+  *	  Opclass validator for bloom.
+  *
+  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blvalidate.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/amvalidate.h"
+ #include "access/htup_details.h"
+ #include "catalog/pg_amop.h"
+ #include "catalog/pg_amproc.h"
+ #include "catalog/pg_opclass.h"
+ #include "catalog/pg_opfamily.h"
+ #include "catalog/pg_type.h"
+ #include "utils/builtins.h"
+ #include "utils/lsyscache.h"
+ #include "utils/syscache.h"
+ 
+ #include "bloom.h"
+ 
+ /*
+  * Validator for a bloom opclass.
+  */
+ bool
+ blvalidate(Oid opclassoid)
+ {
+ 	bool		result = true;
+ 	HeapTuple	classtup;
+ 	Form_pg_opclass classform;
+ 	Oid			opfamilyoid;
+ 	Oid			opcintype;
+ 	Oid			opckeytype;
+ 	char	   *opclassname;
+ 	HeapTuple	familytup;
+ 	Form_pg_opfamily familyform;
+ 	char	   *opfamilyname;
+ 	CatCList   *proclist,
+ 			   *oprlist;
+ 	List	   *grouplist;
+ 	OpFamilyOpFuncGroup *opclassgroup;
+ 	int			i;
+ 	ListCell   *lc;
+ 
+ 	/* Fetch opclass information */
+ 	classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
+ 	if (!HeapTupleIsValid(classtup))
+ 		elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
+ 	classform = (Form_pg_opclass) GETSTRUCT(classtup);
+ 
+ 	opfamilyoid = classform->opcfamily;
+ 	opcintype = classform->opcintype;
+ 	opckeytype = classform->opckeytype;
+ 	if (!OidIsValid(opckeytype))
+ 		opckeytype = opcintype;
+ 	opclassname = NameStr(classform->opcname);
+ 
+ 	/* Fetch opfamily information */
+ 	familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
+ 	if (!HeapTupleIsValid(familytup))
+ 		elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
+ 	familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
+ 
+ 	opfamilyname = NameStr(familyform->opfname);
+ 
+ 	/* Fetch all operators and support functions of the opfamily */
+ 	oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
+ 	proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
+ 
+ 	/* Check individual support functions */
+ 	for (i = 0; i < proclist->n_members; i++)
+ 	{
+ 		HeapTuple	proctup = &proclist->members[i]->tuple;
+ 		Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
+ 		bool		ok;
+ 
+ 		/*
+ 		 * All bloom support functions should be registered with matching
+ 		 * left/right types
+ 		 */
+ 		if (procform->amproclefttype != procform->amprocrighttype)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains support procedure %s with cross-type registration",
+ 							opfamilyname,
+ 							format_procedure(procform->amproc))));
+ 			result = false;
+ 		}
+ 
+ 		/*
+ 		 * We can't check signatures except within the specific opclass, since
+ 		 * we need to know the associated opckeytype in many cases.
+ 		 */
+ 		if (procform->amproclefttype != opcintype)
+ 			continue;
+ 
+ 		/* Check procedure numbers and function signatures */
+ 		switch (procform->amprocnum)
+ 		{
+ 			case BLOOM_HASH_PROC:
+ 				ok = check_amproc_signature(procform->amproc, INT4OID, false,
+ 											1, 1, opckeytype);
+ 				break;
+ 			default:
+ 				ereport(INFO,
+ 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 						 errmsg("bloom opfamily %s contains function %s with invalid support number %d",
+ 								opfamilyname,
+ 								format_procedure(procform->amproc),
+ 								procform->amprocnum)));
+ 				result = false;
+ 				continue;		/* don't want additional message */
+ 		}
+ 
+ 		if (!ok)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("gist opfamily %s contains function %s with wrong signature for support number %d",
+ 							opfamilyname,
+ 							format_procedure(procform->amproc),
+ 							procform->amprocnum)));
+ 			result = false;
+ 		}
+ 	}
+ 
+ 	/* Check individual operators */
+ 	for (i = 0; i < oprlist->n_members; i++)
+ 	{
+ 		HeapTuple	oprtup = &oprlist->members[i]->tuple;
+ 		Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
+ 
+ 		/* TODO: Check that only allowed strategy numbers exist */
+ 		if (oprform->amopstrategy < 1)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains operator %s with invalid strategy number %d",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr),
+ 							oprform->amopstrategy)));
+ 			result = false;
+ 		}
+ 
+ 		/* bloom doesn't support ORDER BY operators */
+ 		if (oprform->amoppurpose != AMOP_SEARCH ||
+ 			OidIsValid(oprform->amopsortfamily))
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains invalid ORDER BY specification for operator %s",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr))));
+ 			result = false;
+ 		}
+ 
+ 		/* Check operator signature --- same for all bloom strategies */
+ 		if (!check_amop_signature(oprform->amopopr, BOOLOID,
+ 								  oprform->amoplefttype,
+ 								  oprform->amoprighttype))
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains operator %s with wrong signature",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr))));
+ 			result = false;
+ 		}
+ 	}
+ 
+ 	/* Now check for inconsistent groups of operators/functions */
+ 	grouplist = identify_opfamily_groups(oprlist, proclist);
+ 	opclassgroup = NULL;
+ 	foreach(lc, grouplist)
+ 	{
+ 		OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
+ 
+ 		/* Remember the group exactly matching the test opclass */
+ 		if (thisgroup->lefttype == opcintype &&
+ 			thisgroup->righttype == opcintype)
+ 			opclassgroup = thisgroup;
+ 
+ 		/*
+ 		 * There is not a lot we can do to check the operator sets, since each
+ 		 * bloom opclass is more or less a law unto itself, and some contain
+ 		 * only operators that are binary-compatible with the opclass datatype
+ 		 * (meaning that empty operator sets can be OK).  That case also means
+ 		 * that we shouldn't insist on nonempty function sets except for the
+ 		 * opclass's own group.
+ 		 */
+ 	}
+ 
+ 	/* Check that the originally-named opclass is complete */
+ 	for (i = 1; i <= BLOOM_NPROC; i++)
+ 	{
+ 		if (opclassgroup &&
+ 			(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
+ 			continue;			/* got it */
+ 		ereport(INFO,
+ 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 				 errmsg("bloom opclass %s is missing support function %d",
+ 						opclassname, i)));
+ 		result = false;
+ 	}
+ 
+ 	ReleaseCatCacheList(proclist);
+ 	ReleaseCatCacheList(oprlist);
+ 	ReleaseSysCache(familytup);
+ 	ReleaseSysCache(classtup);
+ 
+ 	return result;
+ }
diff --git a/contrib/bloom/data/data b/contrib/bloom/data/data
new file mode 100644
index ...eacf3e7
*** a/contrib/bloom/data/data
--- b/contrib/bloom/data/data
***************
*** 0 ****
--- 1,10000 ----
+ 739	3
+ 475	9
+ 45	6
+ 433	1
+ 948	8
+ 926	8
+ 397	7
+ 980	4
+ 212	5
+ 522	9
+ 74	8
+ 77	4
+ 378	9
+ 575	3
+ 625	2
+ 407	4
+ 509	9
+ 252	6
+ 487	7
+ 656	4
+ 485	6
+ 275	9
+ 285	3
+ 277	5
+ 804	8
+ 424	9
+ 553	5
+ 245	9
+ 384	8
+ 202	0
+ 43	6
+ 374	6
+ 490	6
+ 105	10
+ 311	8
+ 411	8
+ 343	7
+ 678	6
+ 942	10
+ 126	7
+ 755	6
+ 625	3
+ 52	9
+ 239	4
+ 690	4
+ 445	7
+ 49	8
+ 285	5
+ 445	4
+ 516	8
+ 151	9
+ 553	5
+ 613	2
+ 123	6
+ 187	2
+ 301	9
+ 800	9
+ 250	9
+ 796	5
+ 288	5
+ 930	7
+ 219	10
+ 553	5
+ 518	10
+ 893	0
+ 754	0
+ 960	3
+ 538	6
+ 537	7
+ 127	7
+ 910	4
+ 666	7
+ 354	9
+ 562	1
+ 405	8
+ 635	3
+ 583	9
+ 313	1
+ 358	8
+ 133	3
+ 864	9
+ 296	8
+ 193	8
+ 396	7
+ 495	5
+ 454	4
+ 952	1
+ 115	3
+ 36	7
+ 455	4
+ 527	1
+ 775	1
+ 945	1
+ 246	3
+ 920	4
+ 554	8
+ 267	8
+ 608	5
+ 684	0
+ 190	2
+ 527	6
+ 584	5
+ 764	7
+ 785	8
+ 376	2
+ 240	9
+ 330	0
+ 13	3
+ 103	3
+ 578	0
+ 639	1
+ 807	9
+ 982	4
+ 365	7
+ 418	6
+ 844	9
+ 199	4
+ 425	10
+ 127	2
+ 762	5
+ 450	0
+ 406	8
+ 17	4
+ 55	1
+ 678	6
+ 143	3
+ 764	9
+ 222	7
+ 364	6
+ 411	8
+ 142	3
+ 728	3
+ 684	2
+ 303	8
+ 363	1
+ 314	8
+ 67	7
+ 592	1
+ 139	6
+ 204	8
+ 279	3
+ 133	0
+ 296	4
+ 788	7
+ 942	2
+ 441	1
+ 454	2
+ 424	1
+ 322	7
+ 949	7
+ 793	3
+ 497	9
+ 984	1
+ 944	1
+ 736	1
+ 940	0
+ 494	1
+ 57	8
+ 429	8
+ 449	4
+ 44	9
+ 454	5
+ 60	9
+ 636	4
+ 606	6
+ 67	4
+ 848	6
+ 259	8
+ 654	2
+ 955	4
+ 351	9
+ 405	8
+ 968	5
+ 634	4
+ 308	1
+ 767	4
+ 974	2
+ 850	0
+ 99	5
+ 416	7
+ 71	5
+ 103	9
+ 48	4
+ 750	7
+ 565	7
+ 92	9
+ 599	5
+ 760	6
+ 960	4
+ 964	3
+ 478	7
+ 620	5
+ 953	5
+ 485	1
+ 957	9
+ 756	0
+ 383	9
+ 946	4
+ 222	7
+ 133	8
+ 401	2
+ 702	0
+ 723	5
+ 568	7
+ 857	5
+ 951	3
+ 264	6
+ 786	2
+ 42	3
+ 268	10
+ 172	0
+ 26	6
+ 884	10
+ 986	1
+ 667	1
+ 893	1
+ 344	6
+ 68	1
+ 58	6
+ 750	9
+ 168	7
+ 249	4
+ 273	0
+ 649	3
+ 306	9
+ 314	5
+ 942	3
+ 33	8
+ 311	0
+ 932	10
+ 138	8
+ 47	5
+ 420	1
+ 550	5
+ 751	3
+ 392	9
+ 1	6
+ 351	3
+ 677	10
+ 588	10
+ 917	9
+ 461	9
+ 242	5
+ 685	6
+ 514	6
+ 531	7
+ 442	6
+ 135	9
+ 693	7
+ 341	4
+ 984	7
+ 362	10
+ 375	7
+ 259	1
+ 713	8
+ 35	6
+ 750	5
+ 489	10
+ 991	2
+ 544	5
+ 791	1
+ 156	2
+ 653	3
+ 96	3
+ 976	4
+ 789	10
+ 170	2
+ 946	5
+ 865	2
+ 597	6
+ 53	6
+ 209	8
+ 128	7
+ 794	1
+ 871	3
+ 623	7
+ 413	8
+ 895	1
+ 70	10
+ 411	0
+ 428	2
+ 6	6
+ 352	10
+ 143	2
+ 156	7
+ 795	2
+ 372	0
+ 11	5
+ 701	8
+ 619	6
+ 142	2
+ 233	6
+ 20	1
+ 621	1
+ 118	0
+ 136	5
+ 232	1
+ 145	6
+ 93	3
+ 800	2
+ 28	6
+ 457	4
+ 598	5
+ 900	3
+ 273	5
+ 870	4
+ 760	1
+ 970	8
+ 231	6
+ 871	3
+ 622	0
+ 895	9
+ 148	0
+ 439	2
+ 328	2
+ 489	4
+ 834	9
+ 756	4
+ 415	7
+ 730	7
+ 175	6
+ 102	9
+ 703	1
+ 715	9
+ 662	6
+ 283	3
+ 592	2
+ 139	7
+ 218	6
+ 981	5
+ 816	5
+ 902	7
+ 417	7
+ 82	8
+ 314	8
+ 519	5
+ 413	6
+ 424	1
+ 693	1
+ 50	4
+ 725	3
+ 639	3
+ 511	8
+ 58	7
+ 356	0
+ 275	2
+ 510	2
+ 822	9
+ 835	9
+ 759	1
+ 717	3
+ 639	1
+ 899	1
+ 246	6
+ 202	3
+ 946	9
+ 629	6
+ 245	1
+ 363	3
+ 870	7
+ 342	1
+ 891	9
+ 322	7
+ 778	2
+ 617	5
+ 307	3
+ 814	9
+ 463	7
+ 8	7
+ 304	2
+ 5	2
+ 138	6
+ 835	4
+ 774	2
+ 685	6
+ 916	0
+ 789	8
+ 878	1
+ 519	7
+ 269	1
+ 193	6
+ 470	0
+ 522	9
+ 720	5
+ 643	0
+ 741	6
+ 275	9
+ 282	1
+ 261	1
+ 306	9
+ 701	2
+ 973	5
+ 28	9
+ 602	5
+ 507	9
+ 683	7
+ 448	2
+ 708	10
+ 88	4
+ 501	7
+ 453	2
+ 379	7
+ 120	7
+ 836	4
+ 717	1
+ 327	4
+ 365	3
+ 908	4
+ 151	5
+ 940	7
+ 381	6
+ 359	8
+ 777	1
+ 799	9
+ 495	3
+ 595	9
+ 542	10
+ 675	7
+ 634	5
+ 44	4
+ 654	4
+ 769	0
+ 671	7
+ 411	8
+ 187	4
+ 481	6
+ 974	8
+ 398	8
+ 907	2
+ 615	4
+ 497	2
+ 349	0
+ 183	0
+ 701	8
+ 535	7
+ 169	2
+ 116	9
+ 208	8
+ 615	6
+ 610	8
+ 970	1
+ 371	9
+ 931	8
+ 695	8
+ 966	3
+ 239	5
+ 520	6
+ 502	7
+ 612	2
+ 520	1
+ 948	7
+ 337	1
+ 627	5
+ 852	2
+ 165	5
+ 45	1
+ 554	4
+ 79	5
+ 185	8
+ 323	2
+ 84	6
+ 613	6
+ 151	1
+ 306	8
+ 318	8
+ 911	3
+ 516	2
+ 331	1
+ 793	2
+ 385	10
+ 646	4
+ 92	2
+ 846	2
+ 686	0
+ 945	0
+ 181	0
+ 572	8
+ 633	7
+ 909	9
+ 486	2
+ 766	4
+ 493	3
+ 645	8
+ 424	4
+ 8	8
+ 395	7
+ 240	5
+ 855	1
+ 659	5
+ 117	6
+ 551	3
+ 634	1
+ 93	3
+ 846	0
+ 206	3
+ 228	10
+ 730	7
+ 253	4
+ 546	7
+ 813	6
+ 487	2
+ 209	7
+ 696	1
+ 814	4
+ 605	9
+ 959	2
+ 230	6
+ 278	3
+ 860	1
+ 324	1
+ 457	6
+ 37	2
+ 273	3
+ 561	8
+ 968	4
+ 373	5
+ 582	6
+ 183	3
+ 646	10
+ 633	3
+ 928	6
+ 407	2
+ 185	7
+ 480	0
+ 810	8
+ 111	3
+ 355	1
+ 453	6
+ 439	0
+ 447	4
+ 388	8
+ 862	10
+ 402	0
+ 247	0
+ 42	9
+ 299	10
+ 472	7
+ 127	7
+ 392	6
+ 702	2
+ 410	8
+ 468	8
+ 960	9
+ 394	4
+ 935	8
+ 806	3
+ 661	7
+ 292	1
+ 714	5
+ 111	8
+ 419	4
+ 725	9
+ 117	9
+ 548	5
+ 460	2
+ 711	9
+ 62	2
+ 636	0
+ 99	0
+ 421	0
+ 870	2
+ 357	5
+ 896	6
+ 594	6
+ 189	7
+ 365	6
+ 116	1
+ 499	2
+ 943	0
+ 742	4
+ 297	5
+ 273	4
+ 631	9
+ 382	7
+ 938	8
+ 765	8
+ 30	1
+ 338	9
+ 771	9
+ 535	10
+ 637	9
+ 568	8
+ 990	1
+ 986	9
+ 114	7
+ 335	4
+ 181	6
+ 770	8
+ 517	2
+ 543	5
+ 954	3
+ 262	10
+ 430	6
+ 910	2
+ 531	4
+ 160	2
+ 345	7
+ 921	3
+ 795	9
+ 267	9
+ 635	6
+ 319	8
+ 211	1
+ 628	7
+ 240	2
+ 182	2
+ 479	4
+ 179	9
+ 43	1
+ 110	6
+ 535	3
+ 743	9
+ 999	7
+ 214	8
+ 571	5
+ 702	2
+ 85	0
+ 22	3
+ 111	6
+ 23	4
+ 821	2
+ 546	3
+ 648	7
+ 210	7
+ 814	3
+ 267	3
+ 590	0
+ 228	6
+ 674	4
+ 382	2
+ 924	1
+ 450	0
+ 106	5
+ 304	2
+ 121	3
+ 568	9
+ 532	1
+ 243	2
+ 839	5
+ 872	7
+ 772	1
+ 2	4
+ 148	2
+ 950	8
+ 673	3
+ 66	6
+ 416	5
+ 606	5
+ 987	9
+ 739	1
+ 237	3
+ 51	8
+ 422	3
+ 949	3
+ 746	8
+ 914	5
+ 959	9
+ 880	1
+ 146	8
+ 929	8
+ 163	10
+ 416	6
+ 511	0
+ 102	5
+ 932	8
+ 606	2
+ 149	7
+ 939	6
+ 951	9
+ 831	7
+ 710	7
+ 215	7
+ 662	1
+ 777	8
+ 926	7
+ 627	1
+ 701	0
+ 668	2
+ 66	8
+ 709	10
+ 611	3
+ 168	8
+ 973	1
+ 330	9
+ 996	2
+ 620	7
+ 907	8
+ 375	6
+ 931	2
+ 377	9
+ 857	0
+ 945	6
+ 49	6
+ 769	1
+ 384	5
+ 113	10
+ 794	3
+ 756	8
+ 389	1
+ 690	4
+ 248	3
+ 90	2
+ 146	5
+ 724	1
+ 616	1
+ 933	5
+ 106	9
+ 31	2
+ 492	8
+ 270	9
+ 279	4
+ 872	1
+ 664	6
+ 840	1
+ 713	5
+ 438	10
+ 841	5
+ 116	10
+ 994	8
+ 63	6
+ 942	10
+ 83	0
+ 874	1
+ 203	4
+ 914	5
+ 242	2
+ 856	1
+ 265	5
+ 742	1
+ 573	5
+ 635	0
+ 416	5
+ 540	5
+ 462	5
+ 373	5
+ 143	3
+ 520	2
+ 363	4
+ 340	6
+ 760	3
+ 40	0
+ 446	9
+ 117	7
+ 416	9
+ 816	10
+ 313	5
+ 0	7
+ 927	5
+ 261	4
+ 74	6
+ 914	2
+ 949	4
+ 443	3
+ 829	8
+ 879	6
+ 37	9
+ 591	5
+ 814	7
+ 195	2
+ 566	0
+ 218	9
+ 462	2
+ 608	4
+ 759	9
+ 778	8
+ 504	7
+ 49	5
+ 127	5
+ 765	10
+ 276	6
+ 544	3
+ 562	1
+ 797	4
+ 843	10
+ 605	4
+ 2	8
+ 288	5
+ 42	9
+ 853	8
+ 765	6
+ 633	3
+ 324	7
+ 722	5
+ 175	5
+ 406	5
+ 130	9
+ 765	7
+ 85	6
+ 68	9
+ 553	7
+ 337	6
+ 497	6
+ 19	5
+ 520	9
+ 340	3
+ 504	10
+ 554	8
+ 656	3
+ 279	8
+ 763	7
+ 283	9
+ 634	0
+ 585	7
+ 609	7
+ 647	2
+ 326	10
+ 717	8
+ 608	7
+ 362	1
+ 608	7
+ 413	1
+ 676	10
+ 940	3
+ 244	2
+ 163	0
+ 903	4
+ 899	5
+ 494	5
+ 256	1
+ 136	9
+ 264	5
+ 886	10
+ 285	5
+ 717	6
+ 621	3
+ 349	0
+ 436	0
+ 2	4
+ 356	2
+ 595	5
+ 251	5
+ 965	2
+ 34	5
+ 633	3
+ 562	8
+ 192	8
+ 231	1
+ 807	5
+ 571	5
+ 163	2
+ 848	5
+ 226	3
+ 536	2
+ 661	9
+ 473	3
+ 412	7
+ 753	4
+ 874	8
+ 837	5
+ 77	4
+ 277	3
+ 225	5
+ 347	0
+ 24	9
+ 555	2
+ 109	4
+ 699	3
+ 688	2
+ 563	3
+ 128	0
+ 604	5
+ 759	4
+ 919	6
+ 143	8
+ 141	2
+ 154	4
+ 488	4
+ 926	8
+ 410	10
+ 752	10
+ 137	9
+ 369	8
+ 197	1
+ 72	8
+ 405	2
+ 795	0
+ 741	6
+ 365	7
+ 187	5
+ 415	3
+ 728	6
+ 745	2
+ 948	7
+ 51	4
+ 621	8
+ 324	8
+ 665	7
+ 595	9
+ 750	7
+ 622	2
+ 867	4
+ 164	6
+ 971	5
+ 267	2
+ 38	7
+ 485	8
+ 251	2
+ 982	2
+ 902	0
+ 556	5
+ 836	9
+ 282	5
+ 573	9
+ 364	3
+ 543	10
+ 477	4
+ 403	6
+ 18	4
+ 171	3
+ 531	2
+ 966	0
+ 974	2
+ 247	10
+ 415	1
+ 988	10
+ 672	8
+ 851	10
+ 325	4
+ 830	7
+ 746	4
+ 675	2
+ 784	1
+ 865	8
+ 450	0
+ 86	10
+ 244	1
+ 998	2
+ 269	2
+ 173	7
+ 394	2
+ 655	1
+ 986	5
+ 20	3
+ 930	8
+ 0	7
+ 224	7
+ 900	0
+ 752	8
+ 809	2
+ 800	9
+ 184	0
+ 947	2
+ 261	2
+ 427	4
+ 899	8
+ 596	6
+ 887	6
+ 60	9
+ 894	10
+ 757	9
+ 667	10
+ 569	6
+ 987	3
+ 331	8
+ 524	1
+ 691	7
+ 174	6
+ 891	4
+ 854	3
+ 870	8
+ 139	5
+ 307	0
+ 48	4
+ 933	9
+ 358	7
+ 836	0
+ 670	4
+ 591	7
+ 726	9
+ 454	3
+ 53	1
+ 959	2
+ 783	8
+ 663	6
+ 168	5
+ 389	3
+ 999	7
+ 334	0
+ 64	3
+ 989	4
+ 957	8
+ 447	6
+ 231	0
+ 285	10
+ 960	7
+ 208	0
+ 883	2
+ 240	7
+ 16	9
+ 302	2
+ 435	7
+ 490	4
+ 388	8
+ 481	5
+ 91	5
+ 874	0
+ 296	3
+ 675	5
+ 359	10
+ 484	3
+ 698	7
+ 332	6
+ 858	6
+ 247	9
+ 475	5
+ 57	9
+ 241	5
+ 344	6
+ 371	8
+ 81	5
+ 296	10
+ 509	6
+ 277	2
+ 120	6
+ 143	6
+ 955	8
+ 296	3
+ 421	2
+ 860	7
+ 28	3
+ 217	1
+ 244	5
+ 632	6
+ 87	0
+ 414	2
+ 465	7
+ 123	10
+ 303	4
+ 158	4
+ 36	3
+ 27	10
+ 142	3
+ 278	6
+ 476	1
+ 231	5
+ 472	4
+ 588	7
+ 907	2
+ 305	10
+ 223	7
+ 161	7
+ 428	3
+ 662	7
+ 684	8
+ 154	7
+ 121	2
+ 711	3
+ 503	10
+ 826	10
+ 127	1
+ 483	6
+ 506	1
+ 316	4
+ 292	6
+ 407	5
+ 339	6
+ 203	8
+ 853	9
+ 499	5
+ 684	7
+ 257	8
+ 833	10
+ 68	3
+ 958	9
+ 316	1
+ 950	8
+ 685	5
+ 870	0
+ 869	2
+ 622	3
+ 676	10
+ 844	9
+ 729	7
+ 743	2
+ 234	4
+ 881	5
+ 233	7
+ 460	3
+ 51	4
+ 193	4
+ 503	1
+ 165	2
+ 600	0
+ 189	5
+ 197	8
+ 745	9
+ 773	6
+ 752	5
+ 285	5
+ 730	5
+ 923	6
+ 10	2
+ 324	5
+ 455	4
+ 888	6
+ 741	4
+ 792	9
+ 579	4
+ 942	8
+ 862	1
+ 580	6
+ 12	4
+ 196	8
+ 854	5
+ 259	6
+ 1	2
+ 195	0
+ 336	5
+ 481	8
+ 894	4
+ 440	6
+ 760	2
+ 542	3
+ 625	5
+ 107	5
+ 622	7
+ 94	6
+ 40	3
+ 397	9
+ 771	7
+ 479	8
+ 837	7
+ 783	2
+ 192	3
+ 964	1
+ 633	4
+ 721	4
+ 637	3
+ 732	3
+ 746	8
+ 749	4
+ 527	8
+ 2	6
+ 133	4
+ 462	9
+ 54	9
+ 677	9
+ 613	5
+ 64	8
+ 725	0
+ 891	4
+ 433	6
+ 751	1
+ 876	5
+ 332	6
+ 324	1
+ 990	9
+ 925	10
+ 418	1
+ 390	9
+ 962	4
+ 820	6
+ 335	4
+ 99	4
+ 239	8
+ 427	1
+ 182	9
+ 743	9
+ 930	6
+ 418	3
+ 241	7
+ 344	2
+ 593	3
+ 223	0
+ 326	6
+ 891	3
+ 58	7
+ 928	4
+ 145	0
+ 792	4
+ 851	2
+ 514	0
+ 80	3
+ 967	0
+ 875	4
+ 272	1
+ 126	6
+ 347	7
+ 884	6
+ 730	2
+ 184	6
+ 498	2
+ 333	4
+ 635	5
+ 453	4
+ 861	3
+ 647	4
+ 338	7
+ 632	3
+ 736	5
+ 689	0
+ 624	8
+ 623	10
+ 534	5
+ 541	3
+ 717	7
+ 885	2
+ 967	2
+ 641	6
+ 696	1
+ 29	6
+ 399	7
+ 933	7
+ 403	6
+ 41	1
+ 73	7
+ 147	7
+ 546	8
+ 668	1
+ 278	2
+ 344	10
+ 934	2
+ 209	9
+ 447	8
+ 503	1
+ 944	5
+ 700	3
+ 208	6
+ 79	6
+ 198	1
+ 750	3
+ 851	9
+ 967	4
+ 668	6
+ 477	9
+ 843	8
+ 940	8
+ 51	1
+ 678	5
+ 999	2
+ 641	9
+ 713	3
+ 285	9
+ 974	4
+ 532	2
+ 485	3
+ 442	3
+ 179	4
+ 733	8
+ 44	2
+ 792	9
+ 32	7
+ 664	1
+ 880	3
+ 581	9
+ 523	2
+ 822	2
+ 563	1
+ 157	5
+ 471	7
+ 709	10
+ 971	2
+ 292	2
+ 561	0
+ 997	6
+ 236	8
+ 491	3
+ 521	2
+ 351	4
+ 498	9
+ 281	0
+ 153	1
+ 258	7
+ 209	4
+ 253	7
+ 105	10
+ 636	1
+ 113	9
+ 227	7
+ 954	2
+ 278	2
+ 14	8
+ 459	5
+ 926	8
+ 937	4
+ 742	2
+ 446	9
+ 320	7
+ 611	5
+ 120	9
+ 210	2
+ 827	8
+ 301	9
+ 775	5
+ 614	7
+ 753	9
+ 918	8
+ 663	4
+ 302	6
+ 187	2
+ 13	9
+ 457	5
+ 824	8
+ 163	4
+ 307	3
+ 300	5
+ 508	1
+ 363	8
+ 67	1
+ 338	7
+ 866	1
+ 573	8
+ 858	2
+ 161	2
+ 824	3
+ 399	8
+ 277	9
+ 295	1
+ 633	5
+ 536	9
+ 742	8
+ 456	3
+ 963	8
+ 61	0
+ 956	4
+ 710	8
+ 490	3
+ 606	3
+ 519	8
+ 508	3
+ 116	9
+ 179	4
+ 762	5
+ 494	4
+ 934	0
+ 335	7
+ 867	8
+ 926	8
+ 610	10
+ 859	6
+ 386	6
+ 389	9
+ 852	10
+ 224	4
+ 763	7
+ 713	9
+ 638	9
+ 272	4
+ 367	8
+ 796	3
+ 796	1
+ 977	7
+ 921	9
+ 493	5
+ 890	4
+ 98	3
+ 921	5
+ 152	8
+ 482	4
+ 143	2
+ 108	9
+ 124	7
+ 750	4
+ 147	1
+ 162	9
+ 418	10
+ 73	4
+ 622	10
+ 298	1
+ 526	2
+ 466	6
+ 464	4
+ 111	6
+ 159	6
+ 992	3
+ 837	1
+ 159	10
+ 847	9
+ 357	10
+ 26	5
+ 937	4
+ 478	0
+ 839	1
+ 5	1
+ 214	5
+ 325	7
+ 156	8
+ 66	3
+ 405	2
+ 859	4
+ 527	7
+ 498	7
+ 658	3
+ 595	0
+ 339	6
+ 535	3
+ 65	0
+ 286	9
+ 112	3
+ 41	3
+ 823	4
+ 6	10
+ 154	1
+ 245	6
+ 295	1
+ 957	8
+ 800	5
+ 508	5
+ 801	1
+ 473	1
+ 723	0
+ 415	8
+ 21	7
+ 691	1
+ 993	7
+ 460	8
+ 97	5
+ 795	3
+ 536	0
+ 811	8
+ 144	8
+ 654	9
+ 224	2
+ 403	0
+ 263	9
+ 165	10
+ 884	6
+ 774	9
+ 282	5
+ 39	3
+ 197	5
+ 91	3
+ 964	9
+ 546	5
+ 926	4
+ 332	1
+ 127	10
+ 15	4
+ 146	4
+ 376	4
+ 293	5
+ 396	2
+ 120	2
+ 83	4
+ 636	1
+ 677	8
+ 620	8
+ 128	6
+ 655	7
+ 84	6
+ 32	4
+ 651	2
+ 400	7
+ 510	5
+ 83	9
+ 957	4
+ 426	4
+ 554	5
+ 523	6
+ 949	2
+ 758	6
+ 992	4
+ 395	1
+ 962	0
+ 794	0
+ 630	8
+ 461	3
+ 984	9
+ 947	5
+ 408	0
+ 380	4
+ 407	8
+ 717	10
+ 352	2
+ 598	3
+ 399	4
+ 927	4
+ 734	3
+ 510	7
+ 371	3
+ 742	0
+ 129	2
+ 283	1
+ 63	2
+ 608	5
+ 261	10
+ 835	7
+ 793	6
+ 628	1
+ 793	2
+ 446	2
+ 582	4
+ 583	3
+ 695	1
+ 13	1
+ 397	8
+ 68	5
+ 957	4
+ 641	0
+ 582	2
+ 491	8
+ 235	3
+ 510	0
+ 879	1
+ 173	7
+ 365	6
+ 863	9
+ 992	4
+ 264	7
+ 540	3
+ 754	9
+ 32	8
+ 464	10
+ 174	1
+ 9	8
+ 353	5
+ 598	6
+ 827	1
+ 616	7
+ 247	8
+ 377	6
+ 407	2
+ 558	4
+ 686	8
+ 86	2
+ 99	8
+ 163	1
+ 662	6
+ 120	8
+ 731	1
+ 591	1
+ 630	2
+ 671	5
+ 298	3
+ 162	5
+ 75	5
+ 155	5
+ 779	7
+ 880	5
+ 535	10
+ 691	6
+ 806	9
+ 764	5
+ 480	9
+ 303	2
+ 13	9
+ 294	6
+ 84	10
+ 100	4
+ 252	3
+ 926	3
+ 801	1
+ 808	6
+ 794	7
+ 45	3
+ 655	7
+ 963	5
+ 589	7
+ 929	1
+ 611	2
+ 279	6
+ 127	6
+ 267	2
+ 538	4
+ 592	8
+ 629	5
+ 117	4
+ 599	9
+ 10	4
+ 614	1
+ 722	3
+ 790	7
+ 730	4
+ 413	7
+ 447	0
+ 891	7
+ 648	0
+ 299	9
+ 228	8
+ 282	8
+ 627	9
+ 338	7
+ 340	9
+ 669	3
+ 330	3
+ 404	1
+ 552	2
+ 738	3
+ 574	2
+ 941	0
+ 174	8
+ 747	8
+ 849	0
+ 738	1
+ 884	0
+ 897	5
+ 931	2
+ 256	3
+ 173	9
+ 621	5
+ 209	0
+ 556	8
+ 220	3
+ 43	8
+ 444	10
+ 815	6
+ 816	6
+ 441	7
+ 609	2
+ 742	5
+ 199	6
+ 4	1
+ 875	3
+ 400	0
+ 185	0
+ 551	4
+ 46	1
+ 155	3
+ 400	2
+ 60	8
+ 183	9
+ 463	10
+ 436	9
+ 665	0
+ 82	4
+ 538	3
+ 47	5
+ 410	9
+ 802	8
+ 970	10
+ 832	5
+ 381	9
+ 627	5
+ 145	0
+ 734	2
+ 872	9
+ 79	3
+ 916	5
+ 238	6
+ 560	3
+ 988	1
+ 602	0
+ 639	0
+ 956	4
+ 823	9
+ 429	7
+ 446	8
+ 533	1
+ 346	7
+ 101	1
+ 883	10
+ 997	10
+ 307	9
+ 477	5
+ 495	0
+ 865	5
+ 135	5
+ 517	8
+ 479	5
+ 215	3
+ 399	6
+ 957	8
+ 454	5
+ 919	8
+ 168	0
+ 880	1
+ 992	9
+ 13	3
+ 791	5
+ 844	3
+ 527	7
+ 768	7
+ 176	3
+ 435	7
+ 759	7
+ 957	2
+ 295	9
+ 4	7
+ 403	9
+ 548	6
+ 943	4
+ 622	9
+ 305	6
+ 235	1
+ 124	1
+ 381	7
+ 789	1
+ 312	10
+ 434	7
+ 619	2
+ 398	6
+ 351	7
+ 489	4
+ 442	9
+ 279	10
+ 463	2
+ 418	1
+ 158	7
+ 720	4
+ 819	8
+ 473	2
+ 496	3
+ 349	8
+ 226	8
+ 556	8
+ 976	10
+ 421	3
+ 648	9
+ 683	1
+ 803	10
+ 80	3
+ 184	5
+ 352	3
+ 221	1
+ 736	0
+ 917	2
+ 240	4
+ 470	6
+ 221	7
+ 372	8
+ 542	3
+ 731	10
+ 676	4
+ 874	4
+ 469	7
+ 321	5
+ 943	5
+ 46	3
+ 848	3
+ 367	6
+ 307	3
+ 793	5
+ 697	3
+ 135	9
+ 959	5
+ 695	5
+ 855	4
+ 464	5
+ 806	3
+ 890	3
+ 14	2
+ 822	10
+ 715	9
+ 253	6
+ 135	6
+ 147	4
+ 904	9
+ 988	6
+ 203	1
+ 519	2
+ 630	2
+ 663	5
+ 640	1
+ 16	4
+ 465	9
+ 720	5
+ 115	5
+ 437	8
+ 410	7
+ 393	5
+ 309	5
+ 987	2
+ 479	10
+ 814	7
+ 97	3
+ 844	7
+ 547	5
+ 212	2
+ 634	2
+ 634	1
+ 133	4
+ 579	2
+ 896	0
+ 79	3
+ 706	5
+ 852	0
+ 12	8
+ 228	5
+ 813	0
+ 173	9
+ 376	0
+ 637	9
+ 524	8
+ 111	2
+ 76	7
+ 258	2
+ 98	8
+ 457	10
+ 853	5
+ 301	6
+ 8	2
+ 574	0
+ 991	8
+ 511	8
+ 845	7
+ 713	2
+ 702	4
+ 144	2
+ 199	3
+ 385	3
+ 999	6
+ 483	1
+ 481	9
+ 91	3
+ 475	4
+ 893	5
+ 544	5
+ 503	5
+ 270	0
+ 338	1
+ 698	1
+ 336	4
+ 402	5
+ 626	6
+ 735	0
+ 875	7
+ 655	4
+ 830	1
+ 298	9
+ 469	8
+ 313	4
+ 256	9
+ 830	8
+ 392	1
+ 773	7
+ 215	5
+ 782	6
+ 871	2
+ 31	5
+ 784	8
+ 509	7
+ 499	2
+ 17	3
+ 299	3
+ 250	8
+ 89	6
+ 130	3
+ 421	10
+ 104	8
+ 59	9
+ 543	3
+ 348	3
+ 824	2
+ 508	9
+ 717	3
+ 620	2
+ 950	1
+ 390	10
+ 448	7
+ 282	7
+ 457	4
+ 262	6
+ 716	7
+ 546	8
+ 496	6
+ 697	0
+ 879	0
+ 363	7
+ 265	9
+ 557	10
+ 163	2
+ 209	1
+ 296	6
+ 80	7
+ 288	4
+ 442	7
+ 733	7
+ 332	4
+ 387	9
+ 269	9
+ 483	10
+ 921	4
+ 12	3
+ 64	3
+ 155	6
+ 260	3
+ 799	5
+ 431	1
+ 68	5
+ 839	4
+ 873	3
+ 101	6
+ 986	4
+ 55	4
+ 311	3
+ 255	8
+ 290	2
+ 155	3
+ 460	2
+ 579	6
+ 840	8
+ 933	6
+ 308	4
+ 735	4
+ 875	6
+ 733	7
+ 855	8
+ 353	8
+ 268	4
+ 213	6
+ 732	5
+ 372	0
+ 644	5
+ 324	1
+ 746	9
+ 718	6
+ 743	7
+ 225	1
+ 15	10
+ 428	9
+ 534	2
+ 637	4
+ 996	10
+ 230	3
+ 399	4
+ 842	1
+ 911	2
+ 153	6
+ 741	5
+ 658	5
+ 380	4
+ 72	1
+ 28	3
+ 174	0
+ 258	6
+ 933	8
+ 763	6
+ 181	8
+ 561	4
+ 22	10
+ 854	9
+ 90	8
+ 78	2
+ 320	8
+ 719	10
+ 305	1
+ 354	4
+ 222	4
+ 675	4
+ 425	9
+ 997	4
+ 725	8
+ 928	9
+ 518	5
+ 317	5
+ 447	2
+ 405	5
+ 936	5
+ 780	3
+ 302	5
+ 233	6
+ 598	6
+ 985	8
+ 969	7
+ 215	4
+ 594	2
+ 752	3
+ 973	7
+ 224	5
+ 167	5
+ 32	6
+ 712	4
+ 152	6
+ 920	9
+ 903	2
+ 430	1
+ 830	0
+ 724	8
+ 848	7
+ 477	1
+ 88	1
+ 276	8
+ 389	2
+ 519	6
+ 740	7
+ 154	8
+ 301	9
+ 209	5
+ 514	1
+ 385	4
+ 351	8
+ 553	2
+ 843	3
+ 998	7
+ 971	5
+ 754	1
+ 545	0
+ 898	9
+ 279	4
+ 547	0
+ 104	7
+ 791	4
+ 568	10
+ 858	1
+ 129	2
+ 499	5
+ 58	1
+ 662	9
+ 330	7
+ 592	3
+ 134	3
+ 359	7
+ 376	3
+ 613	7
+ 675	2
+ 674	8
+ 862	5
+ 183	4
+ 465	0
+ 512	6
+ 284	0
+ 74	3
+ 63	7
+ 244	4
+ 395	8
+ 693	5
+ 181	1
+ 208	6
+ 309	8
+ 212	10
+ 981	9
+ 763	8
+ 352	9
+ 273	8
+ 988	8
+ 410	3
+ 796	5
+ 614	9
+ 220	9
+ 251	6
+ 693	9
+ 144	9
+ 995	4
+ 432	3
+ 174	6
+ 289	2
+ 531	1
+ 998	9
+ 997	3
+ 700	10
+ 57	1
+ 257	9
+ 594	9
+ 711	8
+ 730	10
+ 429	4
+ 905	6
+ 298	9
+ 926	7
+ 205	1
+ 374	5
+ 254	9
+ 545	3
+ 788	5
+ 524	5
+ 528	6
+ 598	8
+ 433	2
+ 656	1
+ 6	4
+ 105	4
+ 809	0
+ 8	1
+ 910	9
+ 836	1
+ 34	2
+ 608	3
+ 115	2
+ 541	9
+ 696	1
+ 391	2
+ 645	10
+ 8	1
+ 180	7
+ 220	2
+ 51	3
+ 621	9
+ 335	6
+ 966	2
+ 564	8
+ 359	6
+ 12	10
+ 887	1
+ 120	4
+ 31	8
+ 492	4
+ 40	1
+ 410	0
+ 213	6
+ 713	4
+ 777	8
+ 759	4
+ 623	1
+ 28	6
+ 338	6
+ 390	7
+ 191	4
+ 663	1
+ 530	8
+ 505	6
+ 599	10
+ 983	6
+ 133	4
+ 687	3
+ 984	4
+ 780	8
+ 163	5
+ 160	8
+ 632	2
+ 374	10
+ 780	8
+ 666	10
+ 167	3
+ 48	7
+ 112	6
+ 258	7
+ 549	2
+ 350	7
+ 635	0
+ 27	6
+ 437	8
+ 380	6
+ 345	5
+ 386	10
+ 727	8
+ 947	5
+ 525	6
+ 477	7
+ 942	5
+ 389	1
+ 77	6
+ 765	6
+ 889	1
+ 308	5
+ 153	3
+ 142	6
+ 143	5
+ 191	5
+ 62	6
+ 465	8
+ 338	4
+ 296	9
+ 25	8
+ 555	10
+ 298	9
+ 20	4
+ 591	8
+ 2	5
+ 901	3
+ 3	1
+ 645	1
+ 645	8
+ 667	8
+ 276	7
+ 413	7
+ 517	8
+ 153	8
+ 613	2
+ 586	2
+ 144	9
+ 112	2
+ 259	7
+ 949	3
+ 183	9
+ 570	2
+ 904	2
+ 331	5
+ 3	10
+ 385	3
+ 726	8
+ 20	2
+ 549	2
+ 56	2
+ 351	6
+ 330	5
+ 525	4
+ 658	8
+ 144	6
+ 45	3
+ 458	6
+ 513	4
+ 830	8
+ 911	8
+ 841	3
+ 112	6
+ 94	1
+ 810	6
+ 305	9
+ 806	7
+ 508	1
+ 150	0
+ 577	8
+ 817	7
+ 416	9
+ 49	9
+ 477	6
+ 236	3
+ 405	1
+ 140	2
+ 443	3
+ 812	5
+ 385	6
+ 181	7
+ 489	10
+ 345	10
+ 122	5
+ 30	7
+ 304	8
+ 421	7
+ 710	5
+ 594	2
+ 31	8
+ 493	4
+ 977	6
+ 681	4
+ 886	5
+ 958	3
+ 116	1
+ 960	6
+ 126	3
+ 602	2
+ 801	6
+ 948	1
+ 480	4
+ 825	2
+ 840	4
+ 376	9
+ 249	9
+ 307	2
+ 504	10
+ 646	4
+ 482	6
+ 661	6
+ 744	6
+ 203	9
+ 927	8
+ 118	7
+ 438	1
+ 833	9
+ 436	7
+ 108	3
+ 77	5
+ 147	3
+ 354	5
+ 552	9
+ 443	2
+ 248	9
+ 802	9
+ 523	5
+ 530	7
+ 416	5
+ 532	5
+ 186	10
+ 600	0
+ 887	0
+ 677	10
+ 312	8
+ 479	5
+ 80	8
+ 913	6
+ 691	4
+ 830	9
+ 281	6
+ 848	8
+ 178	4
+ 530	6
+ 835	1
+ 128	0
+ 31	7
+ 39	9
+ 765	7
+ 914	1
+ 470	4
+ 537	6
+ 226	4
+ 183	9
+ 806	0
+ 855	1
+ 645	7
+ 890	8
+ 81	4
+ 418	9
+ 482	5
+ 937	5
+ 274	10
+ 432	0
+ 692	3
+ 116	2
+ 738	7
+ 713	10
+ 102	9
+ 881	9
+ 909	7
+ 994	6
+ 439	9
+ 378	5
+ 304	8
+ 436	8
+ 341	4
+ 299	6
+ 349	7
+ 653	0
+ 76	8
+ 203	8
+ 421	9
+ 778	5
+ 812	7
+ 431	7
+ 395	4
+ 275	8
+ 309	7
+ 354	6
+ 449	8
+ 398	8
+ 163	7
+ 405	5
+ 428	1
+ 552	5
+ 828	8
+ 319	2
+ 672	1
+ 772	5
+ 756	2
+ 205	2
+ 628	5
+ 986	9
+ 134	3
+ 550	6
+ 130	9
+ 373	3
+ 644	8
+ 805	1
+ 837	4
+ 576	7
+ 113	9
+ 913	8
+ 992	7
+ 270	7
+ 889	5
+ 899	5
+ 956	9
+ 455	1
+ 225	0
+ 673	4
+ 952	0
+ 648	6
+ 825	5
+ 669	7
+ 811	2
+ 326	9
+ 140	2
+ 710	1
+ 925	10
+ 881	8
+ 454	8
+ 331	4
+ 665	8
+ 500	9
+ 791	2
+ 245	7
+ 219	9
+ 339	0
+ 347	0
+ 705	2
+ 253	0
+ 82	4
+ 270	8
+ 526	2
+ 771	4
+ 8	2
+ 186	3
+ 635	9
+ 126	1
+ 741	9
+ 307	10
+ 659	5
+ 878	10
+ 570	2
+ 5	3
+ 383	3
+ 306	5
+ 651	6
+ 256	2
+ 769	0
+ 583	8
+ 251	8
+ 117	9
+ 620	2
+ 21	4
+ 158	3
+ 346	8
+ 854	2
+ 814	4
+ 449	8
+ 699	8
+ 78	0
+ 296	7
+ 580	6
+ 905	3
+ 578	5
+ 127	8
+ 257	2
+ 715	9
+ 486	7
+ 237	6
+ 64	6
+ 461	9
+ 808	3
+ 342	3
+ 95	0
+ 89	2
+ 47	4
+ 901	6
+ 937	8
+ 977	5
+ 294	1
+ 344	6
+ 348	1
+ 428	8
+ 795	7
+ 478	9
+ 249	9
+ 777	1
+ 215	1
+ 314	3
+ 161	4
+ 482	2
+ 787	4
+ 835	7
+ 190	8
+ 238	5
+ 917	6
+ 36	3
+ 641	5
+ 100	4
+ 130	6
+ 295	4
+ 517	1
+ 436	7
+ 191	7
+ 42	4
+ 152	5
+ 559	9
+ 908	4
+ 663	1
+ 207	9
+ 583	1
+ 483	6
+ 390	1
+ 84	5
+ 561	2
+ 67	9
+ 593	6
+ 928	0
+ 316	1
+ 780	4
+ 470	9
+ 882	0
+ 871	8
+ 424	5
+ 888	6
+ 434	5
+ 756	9
+ 90	1
+ 42	2
+ 636	6
+ 387	7
+ 459	10
+ 288	4
+ 11	6
+ 505	8
+ 962	10
+ 722	8
+ 4	6
+ 634	4
+ 125	5
+ 59	6
+ 994	8
+ 476	1
+ 962	5
+ 257	6
+ 121	6
+ 301	6
+ 625	6
+ 966	6
+ 193	5
+ 426	2
+ 445	1
+ 1000	4
+ 739	6
+ 876	9
+ 157	9
+ 424	2
+ 751	9
+ 234	7
+ 418	5
+ 310	5
+ 135	6
+ 118	8
+ 200	1
+ 396	4
+ 555	8
+ 548	0
+ 969	5
+ 449	7
+ 183	3
+ 572	3
+ 261	10
+ 490	0
+ 896	7
+ 724	3
+ 214	0
+ 853	3
+ 645	10
+ 109	8
+ 56	5
+ 237	6
+ 326	8
+ 611	3
+ 334	1
+ 3	5
+ 385	6
+ 856	6
+ 571	3
+ 658	5
+ 69	4
+ 782	3
+ 415	6
+ 633	1
+ 607	7
+ 904	7
+ 248	1
+ 274	6
+ 927	9
+ 869	3
+ 945	9
+ 777	3
+ 447	6
+ 977	0
+ 978	6
+ 485	0
+ 16	3
+ 331	4
+ 902	10
+ 491	5
+ 707	4
+ 172	10
+ 537	4
+ 528	5
+ 331	4
+ 724	3
+ 268	5
+ 607	7
+ 134	6
+ 733	1
+ 219	2
+ 159	2
+ 485	5
+ 666	4
+ 455	2
+ 897	2
+ 552	1
+ 116	1
+ 515	6
+ 552	8
+ 42	3
+ 123	3
+ 777	7
+ 25	9
+ 314	8
+ 23	5
+ 975	2
+ 767	5
+ 673	4
+ 849	1
+ 591	7
+ 290	1
+ 815	4
+ 232	3
+ 51	8
+ 176	1
+ 61	3
+ 403	8
+ 28	4
+ 748	3
+ 185	8
+ 875	2
+ 953	6
+ 621	6
+ 76	5
+ 754	7
+ 216	0
+ 810	0
+ 451	0
+ 360	5
+ 826	5
+ 596	9
+ 834	10
+ 724	9
+ 426	5
+ 205	6
+ 244	1
+ 771	2
+ 724	4
+ 823	8
+ 863	6
+ 466	1
+ 622	3
+ 109	1
+ 318	5
+ 576	1
+ 6	2
+ 30	8
+ 170	8
+ 702	6
+ 226	9
+ 207	5
+ 989	10
+ 667	7
+ 372	5
+ 512	2
+ 67	10
+ 313	7
+ 254	4
+ 762	6
+ 892	3
+ 715	9
+ 510	7
+ 738	7
+ 498	4
+ 276	7
+ 348	5
+ 194	3
+ 462	9
+ 49	8
+ 350	6
+ 68	4
+ 539	4
+ 106	8
+ 804	9
+ 365	7
+ 207	1
+ 595	7
+ 824	3
+ 397	3
+ 773	7
+ 47	1
+ 156	2
+ 457	6
+ 101	5
+ 452	5
+ 66	5
+ 869	6
+ 902	10
+ 397	7
+ 844	8
+ 403	1
+ 841	10
+ 768	7
+ 330	2
+ 988	1
+ 837	0
+ 223	10
+ 276	7
+ 611	4
+ 185	1
+ 829	3
+ 583	7
+ 855	5
+ 672	3
+ 190	5
+ 14	6
+ 567	9
+ 590	3
+ 521	9
+ 498	5
+ 22	3
+ 544	2
+ 328	8
+ 925	9
+ 197	1
+ 1	0
+ 361	6
+ 723	2
+ 68	4
+ 469	3
+ 911	5
+ 851	5
+ 338	4
+ 812	9
+ 361	3
+ 368	4
+ 645	9
+ 629	10
+ 732	6
+ 911	9
+ 663	9
+ 955	0
+ 495	7
+ 241	6
+ 74	7
+ 820	10
+ 192	7
+ 462	5
+ 112	3
+ 388	5
+ 584	8
+ 856	2
+ 667	5
+ 201	4
+ 38	1
+ 329	7
+ 24	3
+ 726	5
+ 963	10
+ 81	0
+ 676	9
+ 21	9
+ 573	5
+ 398	7
+ 757	8
+ 157	3
+ 542	0
+ 569	2
+ 498	8
+ 608	5
+ 882	9
+ 238	9
+ 221	10
+ 424	2
+ 931	5
+ 221	6
+ 407	2
+ 476	10
+ 725	9
+ 664	5
+ 660	8
+ 822	2
+ 835	4
+ 411	3
+ 160	0
+ 870	0
+ 956	1
+ 947	2
+ 73	4
+ 362	0
+ 877	6
+ 612	3
+ 824	1
+ 265	5
+ 963	9
+ 31	6
+ 751	9
+ 825	6
+ 243	2
+ 920	4
+ 256	8
+ 445	2
+ 898	4
+ 390	10
+ 764	8
+ 975	6
+ 335	6
+ 926	2
+ 675	2
+ 708	6
+ 121	7
+ 261	9
+ 592	1
+ 458	8
+ 323	4
+ 238	6
+ 167	7
+ 791	1
+ 75	2
+ 36	8
+ 933	0
+ 481	3
+ 597	4
+ 427	3
+ 598	1
+ 910	7
+ 874	2
+ 590	5
+ 258	0
+ 300	6
+ 425	5
+ 160	6
+ 221	10
+ 657	3
+ 131	7
+ 134	1
+ 703	6
+ 332	3
+ 22	8
+ 573	6
+ 894	5
+ 339	8
+ 655	9
+ 234	9
+ 978	5
+ 494	4
+ 73	7
+ 995	3
+ 603	7
+ 588	7
+ 345	7
+ 799	0
+ 338	1
+ 349	4
+ 889	9
+ 980	8
+ 404	3
+ 551	1
+ 249	8
+ 972	2
+ 319	5
+ 629	4
+ 118	6
+ 685	7
+ 277	3
+ 456	6
+ 996	3
+ 670	3
+ 385	0
+ 694	3
+ 940	7
+ 57	3
+ 993	6
+ 404	2
+ 392	4
+ 468	7
+ 840	1
+ 103	10
+ 721	8
+ 680	10
+ 61	1
+ 620	1
+ 392	3
+ 391	8
+ 310	1
+ 51	3
+ 759	1
+ 595	8
+ 716	10
+ 993	1
+ 374	5
+ 819	2
+ 558	9
+ 172	3
+ 710	9
+ 278	8
+ 989	9
+ 829	4
+ 188	2
+ 158	5
+ 305	2
+ 748	1
+ 317	3
+ 815	0
+ 341	8
+ 141	7
+ 270	10
+ 929	8
+ 883	1
+ 108	6
+ 954	4
+ 364	9
+ 283	2
+ 324	5
+ 413	5
+ 970	7
+ 691	7
+ 781	0
+ 61	6
+ 41	4
+ 405	2
+ 118	7
+ 142	0
+ 504	0
+ 148	6
+ 618	1
+ 997	10
+ 46	3
+ 175	4
+ 752	6
+ 852	7
+ 306	5
+ 440	1
+ 552	5
+ 684	6
+ 904	1
+ 775	0
+ 764	9
+ 68	3
+ 942	2
+ 880	6
+ 319	9
+ 542	4
+ 157	7
+ 734	9
+ 306	6
+ 632	6
+ 130	1
+ 699	7
+ 574	4
+ 275	5
+ 472	1
+ 500	2
+ 968	6
+ 505	9
+ 785	4
+ 470	1
+ 261	0
+ 468	4
+ 730	2
+ 328	0
+ 788	10
+ 648	9
+ 32	3
+ 601	6
+ 730	9
+ 83	2
+ 926	6
+ 439	9
+ 151	9
+ 803	9
+ 327	3
+ 39	6
+ 285	5
+ 7	0
+ 709	3
+ 52	5
+ 294	7
+ 416	3
+ 47	0
+ 931	8
+ 892	0
+ 979	8
+ 597	4
+ 712	7
+ 361	5
+ 685	7
+ 789	7
+ 277	1
+ 231	3
+ 89	9
+ 618	1
+ 437	9
+ 840	9
+ 237	9
+ 869	2
+ 664	8
+ 181	6
+ 580	8
+ 61	3
+ 527	4
+ 808	2
+ 110	6
+ 936	4
+ 671	2
+ 670	8
+ 106	3
+ 901	5
+ 199	7
+ 395	4
+ 629	3
+ 603	3
+ 26	8
+ 936	6
+ 562	10
+ 898	1
+ 418	7
+ 301	5
+ 303	2
+ 915	10
+ 403	6
+ 733	5
+ 873	6
+ 52	1
+ 376	4
+ 508	0
+ 712	1
+ 297	7
+ 894	2
+ 344	5
+ 229	2
+ 546	6
+ 948	8
+ 176	3
+ 84	1
+ 225	5
+ 677	10
+ 996	5
+ 592	0
+ 622	10
+ 495	1
+ 972	2
+ 240	3
+ 944	1
+ 502	3
+ 591	7
+ 530	1
+ 379	5
+ 984	6
+ 730	1
+ 646	10
+ 555	3
+ 912	6
+ 873	5
+ 598	5
+ 472	1
+ 625	4
+ 299	9
+ 713	2
+ 1000	2
+ 531	6
+ 946	1
+ 728	3
+ 540	7
+ 881	3
+ 781	5
+ 223	3
+ 850	1
+ 885	7
+ 639	5
+ 218	1
+ 576	8
+ 555	9
+ 707	3
+ 120	7
+ 483	7
+ 298	4
+ 712	0
+ 755	3
+ 739	6
+ 521	5
+ 162	7
+ 854	0
+ 880	7
+ 735	5
+ 223	10
+ 630	8
+ 795	2
+ 676	5
+ 454	8
+ 208	9
+ 446	5
+ 367	2
+ 532	1
+ 410	3
+ 757	9
+ 789	9
+ 676	6
+ 931	6
+ 384	7
+ 74	6
+ 618	7
+ 407	4
+ 890	1
+ 915	3
+ 878	1
+ 281	3
+ 629	6
+ 482	2
+ 769	9
+ 431	5
+ 824	2
+ 445	5
+ 865	4
+ 55	2
+ 42	1
+ 855	7
+ 834	3
+ 73	7
+ 344	10
+ 68	2
+ 111	3
+ 545	7
+ 996	0
+ 901	8
+ 920	3
+ 291	7
+ 553	7
+ 243	4
+ 111	3
+ 666	2
+ 427	5
+ 812	3
+ 783	9
+ 985	1
+ 873	1
+ 348	10
+ 401	9
+ 724	4
+ 921	6
+ 163	8
+ 957	5
+ 584	5
+ 189	8
+ 928	3
+ 125	6
+ 452	6
+ 114	3
+ 814	9
+ 150	8
+ 23	0
+ 851	4
+ 7	3
+ 265	7
+ 650	2
+ 356	8
+ 25	3
+ 266	6
+ 824	5
+ 436	8
+ 754	6
+ 345	2
+ 114	5
+ 471	9
+ 356	6
+ 726	4
+ 644	6
+ 751	7
+ 829	0
+ 383	5
+ 201	7
+ 291	2
+ 53	6
+ 836	9
+ 11	3
+ 628	8
+ 834	10
+ 972	9
+ 433	4
+ 875	8
+ 63	6
+ 170	7
+ 178	9
+ 359	0
+ 937	7
+ 487	1
+ 481	8
+ 365	5
+ 335	2
+ 411	3
+ 472	0
+ 112	3
+ 13	1
+ 254	4
+ 526	1
+ 236	6
+ 730	4
+ 297	9
+ 327	7
+ 916	3
+ 399	4
+ 402	9
+ 181	8
+ 414	5
+ 967	8
+ 862	4
+ 865	10
+ 745	9
+ 58	10
+ 325	6
+ 128	6
+ 173	9
+ 967	5
+ 767	3
+ 127	7
+ 558	5
+ 86	10
+ 405	3
+ 726	8
+ 783	7
+ 645	6
+ 132	5
+ 619	9
+ 388	7
+ 876	7
+ 260	0
+ 273	4
+ 862	2
+ 903	6
+ 534	0
+ 312	1
+ 555	4
+ 51	10
+ 665	8
+ 780	4
+ 469	4
+ 93	6
+ 934	7
+ 477	3
+ 388	4
+ 34	6
+ 356	3
+ 81	2
+ 546	10
+ 847	1
+ 15	2
+ 171	6
+ 558	2
+ 531	2
+ 998	3
+ 672	5
+ 735	8
+ 67	7
+ 476	5
+ 991	9
+ 897	0
+ 512	3
+ 332	6
+ 471	9
+ 578	3
+ 958	6
+ 477	1
+ 163	0
+ 351	7
+ 259	3
+ 4	9
+ 816	7
+ 695	9
+ 409	2
+ 427	4
+ 35	3
+ 426	5
+ 576	8
+ 140	0
+ 636	7
+ 365	6
+ 311	8
+ 724	5
+ 877	1
+ 167	1
+ 424	2
+ 67	2
+ 911	8
+ 122	3
+ 932	5
+ 721	10
+ 872	1
+ 514	4
+ 905	7
+ 495	5
+ 372	9
+ 135	7
+ 702	9
+ 156	6
+ 933	3
+ 715	4
+ 495	8
+ 596	4
+ 543	7
+ 726	5
+ 267	4
+ 442	1
+ 595	10
+ 588	5
+ 610	1
+ 40	10
+ 943	2
+ 665	6
+ 34	8
+ 224	10
+ 145	9
+ 324	6
+ 721	9
+ 45	3
+ 638	8
+ 739	9
+ 219	2
+ 45	8
+ 138	6
+ 314	7
+ 717	4
+ 730	7
+ 529	4
+ 305	6
+ 216	5
+ 531	4
+ 468	9
+ 0	2
+ 776	0
+ 453	4
+ 817	2
+ 320	0
+ 373	4
+ 850	5
+ 998	2
+ 259	7
+ 518	10
+ 375	0
+ 384	7
+ 611	6
+ 209	1
+ 961	7
+ 998	10
+ 866	8
+ 7	3
+ 188	8
+ 511	5
+ 861	9
+ 873	7
+ 395	9
+ 875	7
+ 586	4
+ 643	10
+ 441	0
+ 642	1
+ 628	9
+ 195	6
+ 529	2
+ 551	4
+ 967	6
+ 714	2
+ 383	2
+ 663	2
+ 109	5
+ 955	5
+ 408	8
+ 158	10
+ 223	8
+ 956	7
+ 829	6
+ 717	5
+ 449	9
+ 46	10
+ 104	6
+ 373	1
+ 156	1
+ 226	5
+ 313	9
+ 781	4
+ 426	7
+ 926	8
+ 566	1
+ 830	8
+ 886	8
+ 454	7
+ 384	2
+ 172	8
+ 82	2
+ 811	2
+ 816	2
+ 257	10
+ 272	5
+ 509	6
+ 373	3
+ 6	8
+ 27	9
+ 635	6
+ 16	5
+ 382	9
+ 250	8
+ 617	6
+ 7	8
+ 468	1
+ 7	3
+ 275	8
+ 464	5
+ 794	7
+ 16	3
+ 320	4
+ 593	3
+ 189	6
+ 259	8
+ 213	3
+ 288	6
+ 177	5
+ 431	8
+ 173	4
+ 583	6
+ 527	6
+ 920	8
+ 413	4
+ 335	2
+ 120	4
+ 509	4
+ 740	1
+ 767	9
+ 722	0
+ 754	9
+ 301	0
+ 530	5
+ 581	10
+ 273	8
+ 400	9
+ 395	9
+ 446	3
+ 728	9
+ 700	1
+ 65	8
+ 414	6
+ 260	2
+ 676	0
+ 84	4
+ 52	8
+ 334	4
+ 880	9
+ 832	5
+ 826	1
+ 216	2
+ 960	6
+ 152	4
+ 927	9
+ 265	6
+ 943	3
+ 446	4
+ 904	7
+ 511	6
+ 733	6
+ 979	8
+ 433	3
+ 138	3
+ 177	10
+ 775	0
+ 74	10
+ 227	0
+ 603	4
+ 441	5
+ 259	7
+ 157	2
+ 37	6
+ 559	9
+ 309	1
+ 522	0
+ 666	5
+ 827	1
+ 814	10
+ 413	10
+ 935	2
+ 993	0
+ 180	2
+ 44	8
+ 599	5
+ 312	9
+ 191	5
+ 61	2
+ 73	6
+ 169	4
+ 691	7
+ 424	4
+ 192	3
+ 456	0
+ 217	9
+ 997	2
+ 57	10
+ 162	2
+ 210	2
+ 19	8
+ 690	3
+ 668	9
+ 801	7
+ 109	9
+ 350	3
+ 256	0
+ 969	7
+ 399	2
+ 932	9
+ 168	1
+ 724	2
+ 301	8
+ 154	5
+ 19	4
+ 668	0
+ 173	4
+ 370	8
+ 239	2
+ 570	3
+ 45	9
+ 626	3
+ 962	6
+ 982	4
+ 757	9
+ 216	9
+ 63	9
+ 89	4
+ 722	2
+ 828	7
+ 606	5
+ 779	8
+ 854	1
+ 619	1
+ 320	2
+ 441	4
+ 110	1
+ 666	1
+ 663	6
+ 432	4
+ 562	6
+ 344	6
+ 588	4
+ 989	3
+ 676	8
+ 51	3
+ 313	8
+ 61	2
+ 978	7
+ 260	3
+ 870	7
+ 663	10
+ 768	3
+ 50	4
+ 978	5
+ 850	5
+ 129	2
+ 165	7
+ 628	2
+ 27	3
+ 971	1
+ 586	3
+ 907	6
+ 450	9
+ 327	7
+ 184	2
+ 410	8
+ 175	2
+ 177	2
+ 608	2
+ 707	5
+ 694	8
+ 652	9
+ 554	3
+ 13	6
+ 584	10
+ 658	2
+ 267	6
+ 816	7
+ 450	1
+ 428	6
+ 339	8
+ 480	5
+ 16	7
+ 739	6
+ 811	4
+ 82	5
+ 283	7
+ 364	8
+ 15	4
+ 417	6
+ 360	1
+ 769	6
+ 640	6
+ 345	1
+ 728	8
+ 723	1
+ 611	2
+ 581	6
+ 861	3
+ 252	7
+ 767	3
+ 177	1
+ 69	5
+ 887	1
+ 918	3
+ 684	3
+ 380	5
+ 906	0
+ 38	3
+ 110	8
+ 24	8
+ 833	6
+ 37	4
+ 263	9
+ 733	5
+ 570	5
+ 849	7
+ 550	9
+ 288	4
+ 2	2
+ 742	7
+ 484	1
+ 139	4
+ 142	2
+ 640	3
+ 942	7
+ 85	8
+ 300	1
+ 188	6
+ 20	9
+ 76	6
+ 422	9
+ 336	10
+ 843	6
+ 409	8
+ 830	2
+ 531	3
+ 274	7
+ 704	4
+ 846	3
+ 667	8
+ 8	8
+ 564	3
+ 874	8
+ 870	9
+ 674	9
+ 483	1
+ 871	8
+ 68	7
+ 444	5
+ 559	3
+ 629	1
+ 588	9
+ 760	3
+ 318	6
+ 636	10
+ 395	6
+ 737	10
+ 951	6
+ 711	8
+ 505	4
+ 767	10
+ 480	6
+ 807	5
+ 353	3
+ 25	9
+ 525	7
+ 2	1
+ 555	8
+ 405	9
+ 369	0
+ 858	8
+ 684	6
+ 723	6
+ 207	4
+ 456	7
+ 818	2
+ 701	3
+ 862	5
+ 845	2
+ 759	9
+ 127	3
+ 523	1
+ 397	1
+ 892	8
+ 952	3
+ 841	8
+ 25	5
+ 406	7
+ 160	6
+ 181	6
+ 325	10
+ 840	0
+ 297	7
+ 534	1
+ 916	3
+ 13	0
+ 577	5
+ 172	10
+ 615	1
+ 775	6
+ 325	6
+ 377	3
+ 141	8
+ 97	3
+ 396	3
+ 918	7
+ 278	8
+ 747	6
+ 459	3
+ 717	4
+ 574	7
+ 418	2
+ 265	6
+ 125	9
+ 655	9
+ 447	10
+ 516	8
+ 329	7
+ 606	4
+ 959	0
+ 705	9
+ 723	10
+ 634	5
+ 557	1
+ 751	3
+ 468	3
+ 4	9
+ 477	3
+ 477	6
+ 149	1
+ 501	6
+ 111	0
+ 419	4
+ 674	0
+ 867	6
+ 28	6
+ 509	8
+ 554	1
+ 221	1
+ 236	10
+ 385	7
+ 298	4
+ 590	8
+ 657	1
+ 375	8
+ 200	9
+ 402	3
+ 893	8
+ 752	6
+ 847	6
+ 199	9
+ 190	7
+ 626	7
+ 852	8
+ 854	1
+ 819	2
+ 792	1
+ 627	4
+ 891	3
+ 450	3
+ 91	6
+ 142	5
+ 961	0
+ 315	7
+ 602	2
+ 331	8
+ 37	5
+ 510	7
+ 265	4
+ 509	1
+ 450	3
+ 358	2
+ 445	10
+ 624	3
+ 270	1
+ 602	4
+ 724	7
+ 854	7
+ 779	2
+ 397	4
+ 331	7
+ 182	4
+ 248	7
+ 31	5
+ 55	5
+ 632	5
+ 869	10
+ 746	3
+ 976	4
+ 649	2
+ 445	3
+ 606	2
+ 995	5
+ 853	8
+ 629	2
+ 155	10
+ 977	3
+ 329	2
+ 30	4
+ 738	1
+ 901	4
+ 589	8
+ 361	3
+ 84	3
+ 706	7
+ 582	2
+ 984	2
+ 319	10
+ 648	2
+ 753	3
+ 421	9
+ 237	4
+ 246	6
+ 623	3
+ 926	4
+ 361	8
+ 732	9
+ 597	1
+ 285	7
+ 430	10
+ 414	0
+ 141	4
+ 201	5
+ 378	8
+ 631	1
+ 126	1
+ 40	4
+ 449	3
+ 929	1
+ 562	9
+ 433	9
+ 682	2
+ 872	3
+ 258	2
+ 960	7
+ 147	4
+ 700	3
+ 773	9
+ 747	2
+ 749	4
+ 282	9
+ 429	3
+ 239	9
+ 608	2
+ 949	2
+ 23	4
+ 92	7
+ 546	10
+ 984	8
+ 121	9
+ 491	3
+ 318	2
+ 556	1
+ 91	3
+ 242	8
+ 680	5
+ 716	1
+ 846	10
+ 986	5
+ 123	9
+ 624	1
+ 317	7
+ 851	9
+ 681	8
+ 668	8
+ 778	2
+ 71	1
+ 350	6
+ 186	4
+ 929	4
+ 282	6
+ 952	10
+ 718	8
+ 952	7
+ 252	1
+ 639	9
+ 221	10
+ 593	1
+ 820	3
+ 906	5
+ 76	7
+ 647	1
+ 779	10
+ 774	10
+ 438	7
+ 393	7
+ 312	3
+ 718	0
+ 143	7
+ 734	4
+ 745	4
+ 271	10
+ 330	9
+ 37	1
+ 138	9
+ 637	2
+ 627	3
+ 361	4
+ 281	1
+ 372	7
+ 838	8
+ 440	1
+ 110	2
+ 180	3
+ 828	9
+ 647	6
+ 287	9
+ 538	6
+ 782	6
+ 766	9
+ 518	4
+ 133	1
+ 688	5
+ 551	10
+ 629	9
+ 689	5
+ 688	1
+ 616	8
+ 287	8
+ 50	1
+ 709	7
+ 687	10
+ 616	2
+ 613	4
+ 801	4
+ 316	3
+ 782	4
+ 464	5
+ 944	0
+ 439	6
+ 939	1
+ 39	6
+ 257	7
+ 425	5
+ 451	5
+ 658	2
+ 173	3
+ 157	8
+ 570	8
+ 186	4
+ 148	5
+ 690	9
+ 951	2
+ 400	9
+ 170	8
+ 468	1
+ 967	5
+ 735	2
+ 162	2
+ 768	6
+ 635	4
+ 774	8
+ 771	9
+ 596	3
+ 700	8
+ 712	8
+ 283	4
+ 778	2
+ 556	2
+ 129	7
+ 17	6
+ 834	10
+ 104	6
+ 208	3
+ 729	10
+ 879	4
+ 403	7
+ 171	2
+ 583	8
+ 516	3
+ 548	2
+ 131	8
+ 631	9
+ 66	2
+ 86	2
+ 912	1
+ 792	7
+ 86	9
+ 315	3
+ 161	0
+ 272	0
+ 408	7
+ 693	6
+ 850	3
+ 346	4
+ 559	9
+ 595	7
+ 726	2
+ 598	8
+ 412	7
+ 987	3
+ 786	8
+ 70	9
+ 676	4
+ 167	8
+ 430	4
+ 877	8
+ 113	6
+ 417	10
+ 846	8
+ 330	4
+ 657	9
+ 94	4
+ 150	7
+ 175	6
+ 376	2
+ 886	2
+ 942	10
+ 34	6
+ 342	2
+ 455	8
+ 639	3
+ 610	8
+ 902	0
+ 715	7
+ 789	0
+ 152	4
+ 969	2
+ 829	1
+ 938	0
+ 682	3
+ 166	6
+ 475	1
+ 525	5
+ 725	9
+ 709	2
+ 639	3
+ 511	2
+ 99	4
+ 275	8
+ 160	1
+ 859	3
+ 509	8
+ 558	3
+ 948	5
+ 341	6
+ 809	5
+ 198	3
+ 614	7
+ 792	3
+ 590	5
+ 519	2
+ 848	0
+ 478	9
+ 443	8
+ 761	6
+ 816	6
+ 916	3
+ 448	5
+ 663	4
+ 970	0
+ 26	8
+ 512	2
+ 62	1
+ 947	9
+ 465	5
+ 354	10
+ 766	2
+ 14	2
+ 149	5
+ 996	9
+ 61	8
+ 530	10
+ 138	10
+ 451	8
+ 374	4
+ 806	4
+ 199	3
+ 623	3
+ 443	6
+ 115	9
+ 107	5
+ 893	9
+ 671	9
+ 117	8
+ 365	1
+ 730	4
+ 926	3
+ 403	1
+ 237	9
+ 865	6
+ 275	7
+ 10	5
+ 988	6
+ 736	4
+ 204	9
+ 340	3
+ 321	2
+ 185	10
+ 140	3
+ 812	5
+ 416	5
+ 931	3
+ 802	3
+ 405	0
+ 189	3
+ 650	5
+ 941	7
+ 939	9
+ 295	7
+ 361	5
+ 526	7
+ 810	8
+ 934	10
+ 839	1
+ 297	7
+ 579	7
+ 194	5
+ 54	10
+ 845	5
+ 36	0
+ 730	7
+ 498	7
+ 346	4
+ 602	6
+ 112	10
+ 140	6
+ 665	9
+ 486	6
+ 945	3
+ 673	2
+ 977	3
+ 954	2
+ 763	0
+ 167	6
+ 468	2
+ 641	2
+ 888	1
+ 870	2
+ 576	5
+ 876	7
+ 434	0
+ 326	1
+ 965	8
+ 698	9
+ 136	4
+ 151	1
+ 624	1
+ 284	4
+ 114	5
+ 994	6
+ 654	6
+ 780	5
+ 773	7
+ 777	3
+ 122	7
+ 36	6
+ 669	4
+ 655	6
+ 174	4
+ 544	3
+ 724	7
+ 423	3
+ 801	7
+ 734	9
+ 158	7
+ 497	8
+ 362	3
+ 354	1
+ 928	1
+ 484	0
+ 784	5
+ 605	5
+ 882	3
+ 87	1
+ 613	6
+ 365	3
+ 326	8
+ 685	1
+ 495	4
+ 42	7
+ 148	5
+ 465	5
+ 816	8
+ 646	7
+ 950	1
+ 793	7
+ 649	4
+ 187	5
+ 658	3
+ 587	3
+ 904	10
+ 608	2
+ 740	3
+ 356	2
+ 712	4
+ 888	9
+ 937	4
+ 370	8
+ 172	0
+ 497	1
+ 146	3
+ 855	8
+ 687	0
+ 327	3
+ 315	9
+ 616	2
+ 866	2
+ 449	6
+ 516	8
+ 842	2
+ 203	7
+ 89	1
+ 83	5
+ 893	3
+ 475	4
+ 376	6
+ 679	2
+ 416	4
+ 272	7
+ 711	6
+ 656	3
+ 806	5
+ 550	3
+ 129	1
+ 60	10
+ 295	3
+ 701	4
+ 403	8
+ 843	3
+ 39	3
+ 686	4
+ 940	4
+ 645	4
+ 732	9
+ 99	4
+ 504	8
+ 770	3
+ 278	3
+ 565	4
+ 386	6
+ 377	7
+ 887	1
+ 65	3
+ 861	9
+ 586	9
+ 227	3
+ 315	2
+ 638	10
+ 523	4
+ 878	6
+ 812	4
+ 378	6
+ 693	7
+ 901	3
+ 62	3
+ 881	4
+ 968	8
+ 517	0
+ 58	4
+ 941	6
+ 278	2
+ 917	6
+ 335	6
+ 553	9
+ 923	4
+ 480	7
+ 813	9
+ 316	5
+ 514	2
+ 763	6
+ 504	6
+ 16	5
+ 413	5
+ 505	5
+ 911	4
+ 116	2
+ 614	0
+ 782	9
+ 587	3
+ 806	5
+ 767	3
+ 246	6
+ 145	6
+ 86	7
+ 780	8
+ 236	3
+ 494	3
+ 756	9
+ 785	3
+ 378	7
+ 707	5
+ 885	3
+ 527	7
+ 269	1
+ 4	1
+ 625	8
+ 362	9
+ 351	5
+ 433	4
+ 166	2
+ 287	4
+ 497	8
+ 655	3
+ 688	4
+ 514	1
+ 137	2
+ 561	0
+ 541	1
+ 690	8
+ 202	7
+ 885	8
+ 464	2
+ 698	8
+ 753	1
+ 252	9
+ 345	5
+ 322	8
+ 321	10
+ 95	0
+ 418	6
+ 76	6
+ 829	6
+ 576	4
+ 725	3
+ 180	9
+ 959	1
+ 755	4
+ 311	5
+ 238	1
+ 585	5
+ 983	9
+ 30	3
+ 772	4
+ 283	9
+ 360	7
+ 476	4
+ 256	3
+ 73	8
+ 675	8
+ 98	9
+ 726	1
+ 919	5
+ 480	2
+ 934	7
+ 293	5
+ 208	3
+ 450	2
+ 582	2
+ 588	9
+ 88	9
+ 567	6
+ 384	8
+ 869	5
+ 655	5
+ 255	8
+ 398	10
+ 810	3
+ 461	3
+ 546	4
+ 8	8
+ 915	2
+ 115	4
+ 453	7
+ 586	0
+ 562	7
+ 988	1
+ 238	4
+ 952	1
+ 829	6
+ 650	1
+ 359	0
+ 64	2
+ 365	5
+ 459	9
+ 920	5
+ 749	8
+ 683	9
+ 200	1
+ 561	8
+ 176	1
+ 460	2
+ 252	7
+ 537	2
+ 805	4
+ 810	5
+ 449	2
+ 503	5
+ 338	9
+ 37	8
+ 778	10
+ 264	5
+ 793	9
+ 390	10
+ 83	10
+ 778	3
+ 74	2
+ 424	3
+ 936	10
+ 530	7
+ 326	3
+ 196	8
+ 509	7
+ 287	8
+ 567	3
+ 645	3
+ 282	9
+ 871	1
+ 856	3
+ 68	9
+ 212	8
+ 198	3
+ 84	6
+ 613	0
+ 583	1
+ 761	9
+ 484	10
+ 684	10
+ 657	10
+ 841	2
+ 296	5
+ 569	6
+ 395	4
+ 653	3
+ 702	7
+ 190	9
+ 567	4
+ 201	7
+ 11	8
+ 670	6
+ 957	4
+ 504	4
+ 389	2
+ 435	0
+ 160	3
+ 270	5
+ 761	8
+ 34	2
+ 279	7
+ 407	10
+ 408	6
+ 894	10
+ 986	1
+ 625	10
+ 908	3
+ 592	9
+ 727	1
+ 307	1
+ 285	7
+ 162	4
+ 17	4
+ 900	8
+ 270	9
+ 934	5
+ 622	3
+ 529	0
+ 939	4
+ 5	9
+ 518	6
+ 923	4
+ 925	5
+ 292	7
+ 612	6
+ 768	9
+ 341	9
+ 341	4
+ 362	2
+ 136	6
+ 175	1
+ 181	8
+ 411	7
+ 826	4
+ 134	8
+ 275	7
+ 461	2
+ 79	4
+ 714	4
+ 38	3
+ 970	8
+ 222	3
+ 737	6
+ 668	1
+ 803	8
+ 732	10
+ 873	9
+ 774	3
+ 623	6
+ 635	8
+ 431	9
+ 409	9
+ 108	5
+ 278	8
+ 858	3
+ 147	8
+ 123	4
+ 139	9
+ 931	8
+ 959	7
+ 611	7
+ 712	5
+ 604	5
+ 769	2
+ 86	4
+ 985	5
+ 313	4
+ 408	4
+ 881	7
+ 243	7
+ 2	4
+ 568	1
+ 760	7
+ 985	7
+ 514	9
+ 426	1
+ 636	1
+ 608	2
+ 624	4
+ 467	7
+ 780	5
+ 227	1
+ 846	6
+ 514	7
+ 321	8
+ 467	3
+ 148	0
+ 448	9
+ 742	4
+ 598	3
+ 378	0
+ 380	0
+ 162	10
+ 253	8
+ 365	7
+ 495	1
+ 173	7
+ 238	0
+ 356	8
+ 746	7
+ 510	2
+ 0	7
+ 248	4
+ 565	10
+ 882	2
+ 246	3
+ 187	6
+ 272	3
+ 614	5
+ 134	10
+ 246	6
+ 125	4
+ 350	4
+ 437	7
+ 115	2
+ 384	6
+ 396	4
+ 282	6
+ 833	8
+ 634	7
+ 10	9
+ 973	2
+ 507	2
+ 545	1
+ 771	7
+ 100	0
+ 307	2
+ 435	7
+ 588	9
+ 364	7
+ 55	7
+ 327	5
+ 132	6
+ 95	10
+ 455	7
+ 679	5
+ 609	7
+ 661	1
+ 897	2
+ 237	7
+ 885	3
+ 685	2
+ 563	1
+ 849	2
+ 991	2
+ 853	0
+ 961	2
+ 497	1
+ 788	6
+ 57	2
+ 320	7
+ 708	9
+ 387	4
+ 46	3
+ 575	3
+ 953	5
+ 620	6
+ 652	2
+ 758	5
+ 333	7
+ 714	2
+ 795	7
+ 365	3
+ 767	2
+ 883	8
+ 396	2
+ 559	1
+ 133	9
+ 472	2
+ 232	0
+ 461	2
+ 507	1
+ 823	2
+ 264	6
+ 659	6
+ 329	4
+ 783	1
+ 47	1
+ 416	8
+ 300	3
+ 638	7
+ 502	2
+ 800	6
+ 145	3
+ 814	4
+ 319	3
+ 561	8
+ 356	4
+ 984	6
+ 964	6
+ 218	3
+ 16	0
+ 418	1
+ 148	8
+ 878	4
+ 133	5
+ 144	6
+ 714	9
+ 270	9
+ 217	1
+ 235	5
+ 358	8
+ 362	7
+ 180	3
+ 335	1
+ 989	6
+ 437	0
+ 553	9
+ 69	7
+ 689	9
+ 149	8
+ 463	3
+ 457	2
+ 238	7
+ 36	5
+ 810	3
+ 990	2
+ 67	4
+ 883	2
+ 698	2
+ 390	7
+ 771	8
+ 693	3
+ 683	8
+ 26	4
+ 709	2
+ 194	2
+ 469	7
+ 349	7
+ 377	4
+ 161	2
+ 656	2
+ 355	7
+ 503	2
+ 969	2
+ 456	4
+ 889	2
+ 187	6
+ 553	9
+ 344	6
+ 241	1
+ 754	4
+ 225	2
+ 85	6
+ 929	5
+ 960	1
+ 650	6
+ 241	0
+ 339	7
+ 244	3
+ 946	7
+ 668	8
+ 928	9
+ 418	5
+ 725	8
+ 59	10
+ 815	8
+ 400	0
+ 35	5
+ 614	10
+ 948	6
+ 53	6
+ 190	3
+ 604	5
+ 39	8
+ 838	10
+ 547	5
+ 820	5
+ 361	2
+ 955	1
+ 0	0
+ 52	8
+ 826	5
+ 855	9
+ 938	5
+ 825	9
+ 43	9
+ 484	2
+ 173	1
+ 762	2
+ 935	6
+ 196	5
+ 107	0
+ 957	5
+ 254	9
+ 554	3
+ 926	6
+ 69	8
+ 58	9
+ 614	10
+ 393	4
+ 882	4
+ 317	4
+ 669	5
+ 454	4
+ 701	4
+ 32	9
+ 871	1
+ 913	8
+ 607	2
+ 740	2
+ 421	7
+ 767	5
+ 418	8
+ 414	0
+ 821	8
+ 470	7
+ 244	8
+ 69	9
+ 277	5
+ 345	10
+ 912	4
+ 875	8
+ 516	8
+ 611	1
+ 956	4
+ 285	4
+ 16	1
+ 867	4
+ 878	3
+ 466	7
+ 88	9
+ 401	3
+ 723	5
+ 245	0
+ 992	6
+ 978	9
+ 967	9
+ 687	5
+ 642	3
+ 607	6
+ 648	9
+ 974	7
+ 944	8
+ 98	8
+ 122	6
+ 520	2
+ 500	9
+ 541	2
+ 391	8
+ 223	4
+ 376	2
+ 288	3
+ 55	10
+ 827	7
+ 273	4
+ 294	9
+ 325	3
+ 585	3
+ 108	7
+ 92	2
+ 248	6
+ 440	7
+ 533	10
+ 971	9
+ 767	2
+ 308	1
+ 395	6
+ 487	4
+ 571	3
+ 146	8
+ 747	4
+ 765	1
+ 707	4
+ 342	8
+ 34	4
+ 46	3
+ 46	5
+ 30	6
+ 466	0
+ 504	2
+ 194	8
+ 378	6
+ 408	9
+ 38	10
+ 178	2
+ 823	9
+ 624	6
+ 997	3
+ 939	3
+ 147	10
+ 772	2
+ 255	8
+ 677	3
+ 397	1
+ 286	9
+ 378	5
+ 712	8
+ 69	1
+ 620	1
+ 98	8
+ 291	9
+ 722	9
+ 509	7
+ 245	4
+ 58	4
+ 421	8
+ 584	7
+ 648	3
+ 962	0
+ 405	2
+ 945	8
+ 727	7
+ 538	8
+ 776	2
+ 903	9
+ 956	2
+ 796	7
+ 108	3
+ 397	4
+ 753	5
+ 745	2
+ 285	3
+ 850	9
+ 591	8
+ 977	10
+ 59	9
+ 780	8
+ 578	3
+ 583	4
+ 476	5
+ 228	4
+ 679	0
+ 110	8
+ 329	5
+ 141	1
+ 963	9
+ 255	2
+ 215	1
+ 181	8
+ 917	2
+ 803	10
+ 80	6
+ 763	7
+ 900	3
+ 12	4
+ 831	2
+ 809	5
+ 264	9
+ 297	6
+ 427	4
+ 674	4
+ 324	9
+ 638	5
+ 34	8
+ 346	10
+ 978	1
+ 928	1
+ 730	7
+ 716	6
+ 36	7
+ 7	9
+ 969	8
+ 378	2
+ 735	7
+ 826	2
+ 113	5
+ 552	4
+ 429	2
+ 976	5
+ 10	3
+ 415	10
+ 470	3
+ 46	2
+ 33	8
+ 831	1
+ 490	8
+ 937	5
+ 654	3
+ 691	4
+ 990	5
+ 550	1
+ 17	1
+ 539	4
+ 292	5
+ 909	3
+ 837	3
+ 289	3
+ 666	3
+ 507	7
+ 96	3
+ 769	6
+ 176	7
+ 45	8
+ 21	7
+ 218	0
+ 253	8
+ 113	3
+ 870	7
+ 715	2
+ 167	6
+ 463	0
+ 947	8
+ 312	6
+ 87	8
+ 312	2
+ 157	1
+ 770	3
+ 787	8
+ 163	8
+ 551	4
+ 818	8
+ 150	9
+ 74	0
+ 583	8
+ 182	8
+ 414	6
+ 755	4
+ 397	1
+ 974	5
+ 886	3
+ 668	0
+ 368	4
+ 377	2
+ 253	5
+ 964	8
+ 921	8
+ 609	1
+ 713	7
+ 90	3
+ 472	3
+ 47	9
+ 917	8
+ 247	3
+ 869	2
+ 798	8
+ 508	5
+ 798	9
+ 905	2
+ 32	2
+ 714	10
+ 962	6
+ 777	6
+ 705	5
+ 253	8
+ 787	7
+ 67	8
+ 612	10
+ 636	9
+ 298	5
+ 80	1
+ 259	6
+ 563	1
+ 464	5
+ 232	5
+ 625	9
+ 491	6
+ 581	3
+ 157	3
+ 759	4
+ 82	5
+ 136	1
+ 380	7
+ 132	0
+ 607	4
+ 520	7
+ 526	8
+ 275	1
+ 837	7
+ 556	1
+ 235	2
+ 15	7
+ 768	6
+ 994	9
+ 882	8
+ 336	10
+ 299	5
+ 112	7
+ 220	2
+ 695	8
+ 675	2
+ 513	2
+ 995	8
+ 290	8
+ 527	8
+ 900	8
+ 27	9
+ 488	8
+ 510	5
+ 720	4
+ 235	1
+ 355	5
+ 528	5
+ 213	7
+ 711	9
+ 574	4
+ 122	1
+ 587	1
+ 876	9
+ 948	4
+ 723	8
+ 165	7
+ 764	7
+ 545	3
+ 134	3
+ 666	4
+ 321	0
+ 903	8
+ 489	1
+ 597	2
+ 23	2
+ 586	1
+ 259	2
+ 263	1
+ 50	2
+ 537	8
+ 60	7
+ 522	8
+ 354	1
+ 98	5
+ 331	8
+ 857	7
+ 786	8
+ 501	3
+ 876	1
+ 475	9
+ 269	1
+ 45	5
+ 234	3
+ 662	3
+ 518	2
+ 56	6
+ 900	6
+ 402	3
+ 644	5
+ 743	10
+ 264	6
+ 627	1
+ 360	1
+ 325	2
+ 226	8
+ 134	5
+ 861	2
+ 22	1
+ 486	7
+ 379	0
+ 882	4
+ 582	8
+ 12	10
+ 37	7
+ 484	8
+ 631	7
+ 379	3
+ 799	7
+ 387	1
+ 974	6
+ 925	1
+ 108	8
+ 287	1
+ 881	8
+ 813	3
+ 778	7
+ 695	4
+ 478	7
+ 344	5
+ 364	8
+ 294	10
+ 577	7
+ 254	4
+ 412	6
+ 500	4
+ 254	4
+ 495	4
+ 211	8
+ 491	1
+ 555	3
+ 352	3
+ 1000	0
+ 693	5
+ 755	0
+ 992	1
+ 865	3
+ 114	4
+ 959	4
+ 818	4
+ 9	3
+ 757	3
+ 743	3
+ 625	10
+ 34	1
+ 46	6
+ 421	4
+ 923	4
+ 445	6
+ 898	2
+ 653	9
+ 319	5
+ 175	4
+ 960	1
+ 802	8
+ 505	8
+ 96	3
+ 75	8
+ 515	7
+ 793	5
+ 817	8
+ 139	2
+ 236	1
+ 658	7
+ 677	6
+ 882	3
+ 445	2
+ 848	6
+ 635	8
+ 754	4
+ 586	3
+ 249	7
+ 523	3
+ 522	0
+ 24	3
+ 587	8
+ 153	7
+ 78	4
+ 787	7
+ 71	5
+ 291	10
+ 794	7
+ 155	6
+ 356	8
+ 451	1
+ 228	0
+ 370	5
+ 719	9
+ 801	2
+ 930	8
+ 556	5
+ 667	7
+ 242	7
+ 98	0
+ 481	2
+ 493	8
+ 123	3
+ 508	3
+ 929	9
+ 68	4
+ 974	3
+ 417	3
+ 772	1
+ 237	6
+ 378	2
+ 399	9
+ 683	1
+ 642	9
+ 811	7
+ 954	3
+ 910	4
+ 64	0
+ 734	6
+ 310	7
+ 437	4
+ 43	4
+ 674	5
+ 756	4
+ 596	10
+ 20	10
+ 158	4
+ 907	8
+ 485	5
+ 766	3
+ 290	7
+ 588	2
+ 167	7
+ 233	9
+ 224	5
+ 564	7
+ 922	6
+ 73	6
+ 67	8
+ 41	7
+ 820	1
+ 637	10
+ 480	5
+ 820	10
+ 94	6
+ 260	4
+ 306	8
+ 584	5
+ 500	8
+ 374	7
+ 361	9
+ 385	3
+ 545	5
+ 877	6
+ 286	9
+ 275	1
+ 979	9
+ 85	5
+ 457	9
+ 424	6
+ 492	7
+ 936	8
+ 531	5
+ 271	0
+ 337	6
+ 755	7
+ 583	1
+ 980	1
+ 599	9
+ 739	9
+ 776	0
+ 992	8
+ 926	1
+ 215	4
+ 982	6
+ 935	5
+ 322	9
+ 272	9
+ 391	5
+ 885	7
+ 189	6
+ 426	8
+ 780	4
+ 899	4
+ 264	6
+ 264	0
+ 652	3
+ 796	6
+ 332	0
+ 961	3
+ 649	9
+ 789	10
+ 767	1
+ 825	2
+ 605	7
+ 886	8
+ 349	3
+ 566	1
+ 719	5
+ 508	10
+ 103	8
+ 23	8
+ 28	8
+ 333	4
+ 830	3
+ 675	5
+ 190	5
+ 450	10
+ 525	3
+ 115	1
+ 984	0
+ 924	3
+ 313	5
+ 463	0
+ 955	10
+ 15	1
+ 743	0
+ 813	8
+ 858	1
+ 131	7
+ 440	8
+ 167	6
+ 270	6
+ 587	8
+ 892	7
+ 925	9
+ 702	8
+ 210	0
+ 339	7
+ 47	3
+ 643	1
+ 351	4
+ 101	2
+ 157	10
+ 310	3
+ 647	7
+ 93	8
+ 380	4
+ 432	10
+ 158	3
+ 668	1
+ 201	4
+ 933	4
+ 386	3
+ 83	4
+ 566	7
+ 496	9
+ 113	6
+ 81	3
+ 556	4
+ 557	2
+ 140	7
+ 16	5
+ 13	4
+ 487	2
+ 772	2
+ 253	10
+ 526	2
+ 384	9
+ 458	5
+ 345	0
+ 194	8
+ 941	3
+ 438	0
+ 577	10
+ 413	1
+ 196	6
+ 784	2
+ 74	8
+ 660	6
+ 967	4
+ 716	2
+ 405	2
+ 407	8
+ 154	9
+ 256	5
+ 888	4
+ 341	8
+ 757	8
+ 852	3
+ 771	3
+ 468	10
+ 819	3
+ 179	9
+ 49	8
+ 454	0
+ 271	2
+ 238	7
+ 413	6
+ 465	6
+ 509	7
+ 67	4
+ 171	4
+ 226	9
+ 186	1
+ 261	10
+ 343	7
+ 924	2
+ 982	1
+ 55	0
+ 942	5
+ 48	2
+ 679	3
+ 890	1
+ 930	4
+ 659	4
+ 75	7
+ 836	2
+ 133	1
+ 173	3
+ 141	4
+ 277	5
+ 164	2
+ 646	1
+ 305	7
+ 178	2
+ 210	2
+ 460	9
+ 512	4
+ 981	4
+ 705	6
+ 881	8
+ 366	7
+ 26	5
+ 780	2
+ 818	9
+ 634	1
+ 404	8
+ 296	0
+ 945	6
+ 751	1
+ 848	10
+ 349	3
+ 850	9
+ 658	8
+ 303	4
+ 471	2
+ 143	8
+ 902	2
+ 335	7
+ 368	2
+ 602	0
+ 248	0
+ 800	5
+ 55	7
+ 145	8
+ 868	10
+ 767	2
+ 301	6
+ 78	10
+ 447	4
+ 322	9
+ 566	5
+ 754	5
+ 633	1
+ 149	0
+ 242	8
+ 2	5
+ 757	8
+ 35	8
+ 547	2
+ 618	4
+ 174	4
+ 631	5
+ 1	7
+ 434	4
+ 91	8
+ 366	7
+ 221	1
+ 124	9
+ 208	3
+ 855	5
+ 25	9
+ 941	8
+ 660	10
+ 593	2
+ 157	2
+ 621	3
+ 596	3
+ 806	6
+ 962	2
+ 45	1
+ 996	4
+ 709	2
+ 530	8
+ 72	7
+ 107	9
+ 189	1
+ 784	1
+ 913	4
+ 106	5
+ 650	3
+ 717	3
+ 594	3
+ 524	4
+ 910	5
+ 640	10
+ 538	6
+ 365	2
+ 854	9
+ 80	9
+ 634	2
+ 852	8
+ 318	6
+ 953	2
+ 80	1
+ 737	7
+ 323	5
+ 2	9
+ 766	5
+ 317	7
+ 11	10
+ 630	5
+ 593	10
+ 795	4
+ 891	9
+ 372	5
+ 61	2
+ 348	4
+ 861	3
+ 610	9
+ 360	3
+ 672	7
+ 800	7
+ 599	6
+ 199	9
+ 242	2
+ 873	9
+ 759	5
+ 868	6
+ 912	8
+ 429	3
+ 284	5
+ 507	6
+ 869	4
+ 933	5
+ 309	3
+ 825	10
+ 976	6
+ 654	6
+ 190	9
+ 491	4
+ 63	4
+ 304	8
+ 829	2
+ 377	7
+ 931	8
+ 24	2
+ 295	5
+ 848	2
+ 899	8
+ 642	2
+ 74	5
+ 188	0
+ 92	8
+ 624	3
+ 695	1
+ 714	8
+ 479	0
+ 581	3
+ 191	10
+ 49	1
+ 763	1
+ 337	1
+ 604	2
+ 222	5
+ 965	9
+ 712	0
+ 332	9
+ 88	4
+ 742	7
+ 706	4
+ 828	4
+ 196	3
+ 438	8
+ 616	6
+ 735	7
+ 751	5
+ 738	1
+ 556	3
+ 272	8
+ 846	2
+ 643	6
+ 277	10
+ 457	4
+ 398	2
+ 77	1
+ 636	9
+ 524	8
+ 213	10
+ 609	8
+ 591	3
+ 493	3
+ 842	2
+ 430	4
+ 573	7
+ 177	4
+ 940	8
+ 977	2
+ 795	4
+ 581	2
+ 633	7
+ 297	3
+ 564	8
+ 101	8
+ 783	7
+ 605	4
+ 55	1
+ 716	9
+ 329	1
+ 296	9
+ 847	5
+ 321	8
+ 294	3
+ 3	1
+ 731	6
+ 281	4
+ 243	6
+ 634	8
+ 399	7
+ 583	2
+ 445	2
+ 555	5
+ 286	3
+ 398	6
+ 417	7
+ 517	3
+ 167	8
+ 51	5
+ 135	1
+ 549	9
+ 638	8
+ 231	9
+ 409	9
+ 687	8
+ 599	3
+ 989	0
+ 458	5
+ 545	7
+ 816	9
+ 359	2
+ 637	9
+ 496	8
+ 713	5
+ 265	8
+ 601	8
+ 715	2
+ 645	9
+ 119	1
+ 810	8
+ 862	4
+ 76	9
+ 454	5
+ 395	10
+ 279	2
+ 942	6
+ 442	6
+ 513	9
+ 383	2
+ 486	6
+ 73	1
+ 463	8
+ 325	1
+ 733	4
+ 162	5
+ 251	0
+ 952	3
+ 874	4
+ 862	3
+ 405	1
+ 479	3
+ 778	9
+ 925	3
+ 860	3
+ 516	3
+ 956	6
+ 433	4
+ 377	8
+ 527	1
+ 203	7
+ 654	5
+ 713	6
+ 781	6
+ 12	6
+ 856	4
+ 783	3
+ 763	6
+ 257	7
+ 852	1
+ 995	4
+ 463	10
+ 957	9
+ 369	3
+ 654	9
+ 445	9
+ 584	1
+ 310	3
+ 704	1
+ 884	7
+ 734	7
+ 132	5
+ 75	9
+ 79	3
+ 582	9
+ 449	6
+ 299	9
+ 527	3
+ 808	9
+ 590	5
+ 791	0
+ 318	4
+ 134	6
+ 671	8
+ 721	6
+ 554	5
+ 295	7
+ 973	4
+ 582	1
+ 702	2
+ 983	2
+ 741	3
+ 63	3
+ 538	9
+ 163	1
+ 333	10
+ 164	7
+ 329	3
+ 280	10
+ 136	0
+ 555	7
+ 455	8
+ 377	4
+ 220	10
+ 480	9
+ 122	5
+ 72	9
+ 744	1
+ 130	3
+ 6	3
+ 410	3
+ 248	6
+ 989	6
+ 872	3
+ 577	0
+ 270	1
+ 697	7
+ 981	1
+ 153	2
+ 32	6
+ 122	2
+ 96	2
+ 16	8
+ 329	1
+ 122	3
+ 440	5
+ 673	7
+ 107	7
+ 265	10
+ 932	8
+ 986	2
+ 972	7
+ 927	10
+ 757	1
+ 154	8
+ 713	3
+ 942	8
+ 470	10
+ 649	8
+ 104	8
+ 134	5
+ 305	8
+ 232	4
+ 469	5
+ 390	4
+ 338	4
+ 602	3
+ 58	5
+ 264	8
+ 609	4
+ 603	3
+ 694	5
+ 131	2
+ 503	8
+ 962	6
+ 552	1
+ 152	9
+ 902	4
+ 268	4
+ 881	7
+ 772	2
+ 33	4
+ 529	1
+ 903	8
+ 905	5
+ 210	5
+ 833	9
+ 53	10
+ 67	6
+ 744	0
+ 164	3
+ 125	3
+ 153	0
+ 699	4
+ 398	6
+ 78	2
+ 798	1
+ 544	3
+ 202	4
+ 119	1
+ 959	3
+ 615	8
+ 232	7
+ 756	3
+ 224	5
+ 328	4
+ 797	5
+ 703	10
+ 480	4
+ 371	9
+ 982	4
+ 49	8
+ 561	6
+ 106	8
+ 40	2
+ 869	10
+ 554	5
+ 790	8
+ 151	5
+ 85	4
+ 47	4
+ 763	8
+ 866	5
+ 794	3
+ 868	2
+ 225	8
+ 615	3
+ 629	2
+ 866	7
+ 937	9
+ 960	8
+ 904	5
+ 290	7
+ 301	4
+ 241	4
+ 816	3
+ 799	6
+ 131	7
+ 45	9
+ 12	9
+ 90	2
+ 762	7
+ 510	4
+ 880	4
+ 126	8
+ 282	1
+ 623	2
+ 601	9
+ 880	9
+ 354	1
+ 287	2
+ 408	1
+ 749	5
+ 753	8
+ 464	8
+ 707	6
+ 2	5
+ 258	5
+ 859	1
+ 888	10
+ 956	2
+ 71	6
+ 355	7
+ 492	2
+ 574	8
+ 355	9
+ 15	8
+ 948	8
+ 302	7
+ 558	8
+ 466	3
+ 320	5
+ 733	6
+ 980	6
+ 716	9
+ 577	7
+ 37	6
+ 251	4
+ 321	7
+ 627	9
+ 588	10
+ 756	6
+ 746	7
+ 367	0
+ 405	9
+ 814	9
+ 191	1
+ 338	9
+ 712	3
+ 517	4
+ 186	1
+ 100	2
+ 743	4
+ 615	1
+ 93	2
+ 958	7
+ 225	7
+ 284	10
+ 418	7
+ 19	8
+ 577	8
+ 693	8
+ 967	0
+ 692	7
+ 349	2
+ 106	5
+ 303	2
+ 758	0
+ 557	4
+ 109	7
+ 616	1
+ 332	8
+ 782	6
+ 812	2
+ 267	8
+ 22	8
+ 665	7
+ 612	6
+ 746	3
+ 309	1
+ 512	4
+ 630	8
+ 622	4
+ 860	2
+ 762	10
+ 830	4
+ 37	2
+ 219	8
+ 777	0
+ 19	0
+ 863	0
+ 888	5
+ 756	5
+ 159	5
+ 804	5
+ 597	3
+ 884	2
+ 131	5
+ 616	10
+ 685	4
+ 961	5
+ 756	10
+ 675	10
+ 818	5
+ 6	8
+ 496	9
+ 878	4
+ 397	6
+ 884	6
+ 135	7
+ 23	7
+ 3	9
+ 959	1
+ 412	6
+ 125	1
+ 953	1
+ 611	7
+ 84	3
+ 683	9
+ 739	7
+ 738	2
+ 559	6
+ 619	10
+ 249	5
+ 511	4
+ 190	5
+ 116	2
+ 442	1
+ 327	9
+ 649	5
+ 951	6
+ 538	6
+ 310	6
+ 848	10
+ 524	6
+ 684	3
+ 822	2
+ 878	4
+ 198	1
+ 943	7
+ 512	1
+ 244	6
+ 325	7
+ 702	7
+ 539	4
+ 104	5
+ 952	6
+ 52	3
+ 264	9
+ 257	8
+ 487	9
+ 50	3
+ 183	9
+ 748	4
+ 56	7
+ 91	6
+ 823	3
+ 195	1
+ 21	9
+ 801	6
+ 247	9
+ 50	2
+ 546	1
+ 462	8
+ 2	7
+ 597	5
+ 659	6
+ 797	8
+ 575	5
+ 224	6
+ 236	3
+ 198	1
+ 650	4
+ 208	7
+ 289	0
+ 231	5
+ 913	3
+ 735	5
+ 383	2
+ 268	4
+ 915	9
+ 874	6
+ 512	7
+ 417	1
+ 215	6
+ 718	5
+ 955	9
+ 511	6
+ 309	7
+ 275	6
+ 727	5
+ 133	6
+ 786	9
+ 99	2
+ 64	4
+ 554	10
+ 233	4
+ 554	7
+ 98	10
+ 832	3
+ 611	5
+ 765	6
+ 466	3
+ 170	8
+ 995	4
+ 371	7
+ 951	5
+ 363	7
+ 371	5
+ 907	4
+ 830	5
+ 414	1
+ 889	10
+ 808	10
+ 937	6
+ 301	5
+ 189	1
+ 114	7
+ 343	3
+ 429	3
+ 729	8
+ 61	7
+ 304	4
+ 416	7
+ 886	3
+ 110	7
+ 784	5
+ 779	7
+ 491	6
+ 660	4
+ 226	10
+ 976	4
+ 28	1
+ 71	4
+ 374	5
+ 709	1
+ 300	8
+ 782	6
+ 193	2
+ 280	1
+ 521	4
+ 794	3
+ 913	6
+ 978	4
+ 159	6
+ 833	4
+ 600	8
+ 801	6
+ 899	9
+ 999	3
+ 371	7
+ 376	7
+ 477	2
+ 276	7
+ 356	6
+ 749	9
+ 945	5
+ 183	9
+ 116	2
+ 262	3
+ 799	1
+ 661	4
+ 904	5
+ 28	8
+ 334	0
+ 76	7
+ 735	5
+ 376	2
+ 609	7
+ 882	10
+ 207	6
+ 843	2
+ 174	0
+ 10	3
+ 187	3
+ 565	10
+ 366	2
+ 386	3
+ 689	4
+ 73	0
+ 441	1
+ 727	2
+ 600	1
+ 388	2
+ 756	3
+ 176	10
+ 901	0
+ 115	1
+ 45	1
+ 364	2
+ 396	9
+ 218	8
+ 156	6
+ 32	8
+ 18	1
+ 867	5
+ 254	6
+ 635	9
+ 699	0
+ 65	5
+ 293	2
+ 417	2
+ 259	5
+ 268	3
+ 656	6
+ 535	1
+ 562	8
+ 814	7
+ 357	8
+ 563	4
+ 952	4
+ 834	2
+ 25	5
+ 60	7
+ 492	1
+ 178	8
+ 365	6
+ 977	6
+ 127	2
+ 928	8
+ 877	5
+ 834	4
+ 216	6
+ 157	6
+ 495	7
+ 949	4
+ 150	8
+ 653	2
+ 252	7
+ 898	7
+ 838	1
+ 527	2
+ 671	5
+ 827	8
+ 750	8
+ 581	6
+ 217	4
+ 66	4
+ 64	2
+ 7	6
+ 943	10
+ 6	1
+ 738	7
+ 267	10
+ 372	2
+ 733	2
+ 242	3
+ 413	9
+ 765	2
+ 712	5
+ 994	3
+ 142	2
+ 708	2
+ 645	8
+ 431	7
+ 331	4
+ 608	3
+ 466	3
+ 996	7
+ 336	4
+ 899	1
+ 577	1
+ 330	10
+ 54	1
+ 229	8
+ 609	2
+ 59	8
+ 435	8
+ 959	1
+ 539	4
+ 733	9
+ 763	3
+ 207	2
+ 687	2
+ 961	0
+ 570	9
+ 93	1
+ 1	4
+ 137	1
+ 517	4
+ 821	1
+ 590	9
+ 878	0
+ 646	8
+ 106	2
+ 226	8
+ 56	10
+ 180	3
+ 218	9
+ 466	2
+ 891	0
+ 39	10
+ 183	0
+ 407	3
+ 95	9
+ 686	9
+ 51	3
+ 795	9
+ 301	4
+ 765	4
+ 627	10
+ 246	7
+ 981	4
+ 946	2
+ 294	4
+ 378	2
+ 448	4
+ 170	6
+ 457	6
+ 950	6
+ 501	6
+ 467	6
+ 911	3
+ 480	2
+ 704	2
+ 619	3
+ 237	9
+ 14	2
+ 291	10
+ 416	6
+ 372	8
+ 770	8
+ 212	9
+ 451	7
+ 516	4
+ 221	0
+ 37	7
+ 568	9
+ 950	0
+ 160	7
+ 293	8
+ 985	5
+ 644	10
+ 747	9
+ 960	2
+ 519	3
+ 958	3
+ 151	2
+ 227	6
+ 838	7
+ 3	1
+ 760	0
+ 747	3
+ 988	7
+ 376	1
+ 351	7
+ 928	3
+ 198	6
+ 336	9
+ 506	3
+ 109	0
+ 627	1
+ 312	8
+ 236	5
+ 380	1
+ 283	4
+ 133	0
+ 423	9
+ 371	4
+ 577	7
+ 559	9
+ 415	5
+ 264	6
+ 58	6
+ 559	6
+ 896	7
+ 588	5
+ 734	9
+ 302	10
+ 440	7
+ 45	7
+ 66	2
+ 766	5
+ 58	1
+ 899	6
+ 883	5
+ 562	3
+ 945	8
+ 911	0
+ 427	5
+ 566	3
+ 138	2
+ 847	9
+ 55	1
+ 842	5
+ 832	9
+ 218	9
+ 66	10
+ 386	1
+ 120	3
+ 758	0
+ 743	3
+ 300	7
+ 147	2
+ 690	6
+ 681	3
+ 898	8
+ 411	7
+ 691	5
+ 895	5
+ 960	7
+ 420	2
+ 625	5
+ 162	0
+ 610	3
+ 296	4
+ 283	0
+ 688	6
+ 726	8
+ 794	4
+ 410	5
+ 673	3
+ 294	1
+ 54	10
+ 549	9
+ 518	5
+ 677	9
+ 687	3
+ 425	8
+ 314	0
+ 130	6
+ 402	4
+ 648	1
+ 997	4
+ 926	8
+ 791	3
+ 267	5
+ 645	6
+ 547	7
+ 546	1
+ 649	1
+ 605	3
+ 3	3
+ 628	4
+ 141	9
+ 462	3
+ 551	9
+ 684	2
+ 954	7
+ 574	9
+ 472	4
+ 217	7
+ 828	9
+ 299	4
+ 562	8
+ 471	2
+ 909	1
+ 536	9
+ 367	2
+ 339	5
+ 106	8
+ 779	7
+ 664	5
+ 856	6
+ 144	4
+ 499	6
+ 796	7
+ 353	6
+ 579	7
+ 999	1
+ 497	5
+ 351	4
+ 546	9
+ 317	9
+ 51	7
+ 421	2
+ 456	2
+ 813	1
+ 664	7
+ 738	8
+ 100	2
+ 422	9
+ 953	8
+ 520	5
+ 428	5
+ 672	9
+ 990	0
+ 331	5
+ 910	6
+ 448	10
+ 305	9
+ 118	8
+ 70	9
+ 881	7
+ 601	6
+ 541	7
+ 855	10
+ 597	8
+ 739	1
+ 341	2
+ 637	0
+ 93	6
+ 37	4
+ 162	9
+ 73	6
+ 908	4
+ 480	0
+ 139	6
+ 957	0
+ 284	6
+ 638	8
+ 259	5
+ 788	9
+ 302	5
+ 974	6
+ 695	6
+ 656	8
+ 237	7
+ 212	4
+ 639	3
+ 9	5
+ 663	5
+ 573	8
+ 39	5
+ 821	3
+ 88	5
+ 148	3
+ 952	9
+ 204	3
+ 464	2
+ 896	2
+ 789	6
+ 947	0
+ 244	2
+ 425	9
+ 444	4
+ 430	1
+ 924	0
+ 909	10
+ 533	7
+ 286	6
+ 189	4
+ 969	1
+ 370	2
+ 394	8
+ 350	3
+ 993	1
+ 842	9
+ 165	1
+ 99	6
+ 969	5
+ 24	4
+ 651	9
+ 401	6
+ 911	9
+ 290	2
+ 556	5
+ 631	5
+ 619	0
+ 696	0
+ 835	0
+ 303	8
+ 185	1
+ 767	3
+ 231	9
+ 940	2
+ 410	10
+ 598	1
+ 912	10
+ 621	8
+ 934	9
+ 20	5
+ 389	7
+ 14	0
+ 651	7
+ 22	5
+ 757	3
+ 313	9
+ 471	1
+ 292	7
+ 947	2
+ 902	4
+ 196	5
+ 418	1
+ 500	0
+ 931	4
+ 949	10
+ 924	3
+ 601	9
+ 348	3
+ 648	4
+ 738	4
+ 695	1
+ 347	2
+ 132	6
+ 867	1
+ 872	8
+ 436	1
+ 269	9
+ 176	8
+ 893	1
+ 203	8
+ 59	1
+ 181	7
+ 65	5
+ 912	7
+ 898	7
+ 118	6
+ 702	5
+ 758	8
+ 105	6
+ 913	10
+ 395	3
+ 45	7
+ 204	2
+ 433	1
+ 329	6
+ 939	4
+ 764	1
+ 48	8
+ 650	10
+ 542	5
+ 610	7
+ 141	3
+ 126	9
+ 146	2
+ 525	1
+ 208	9
+ 409	3
+ 584	6
+ 474	0
+ 710	8
+ 654	6
+ 190	4
+ 770	2
+ 247	4
+ 198	8
+ 968	8
+ 448	1
+ 121	6
+ 8	3
+ 805	5
+ 326	0
+ 452	7
+ 265	0
+ 347	7
+ 53	1
+ 542	7
+ 706	7
+ 124	5
+ 970	4
+ 896	2
+ 159	9
+ 977	6
+ 972	1
+ 182	10
+ 364	10
+ 513	7
+ 999	10
+ 425	3
+ 1000	8
+ 3	1
+ 829	5
+ 759	5
+ 277	9
+ 12	2
+ 254	9
+ 415	4
+ 772	4
+ 21	7
+ 490	2
+ 725	9
+ 189	2
+ 544	2
+ 202	10
+ 452	2
+ 741	5
+ 254	6
+ 1000	0
+ 106	3
+ 896	1
+ 523	1
+ 27	9
+ 563	8
+ 330	6
+ 544	8
+ 786	3
+ 674	10
+ 506	2
+ 162	7
+ 186	6
+ 910	9
+ 69	2
+ 496	1
+ 177	6
+ 346	1
+ 720	9
+ 223	7
+ 807	8
+ 546	1
+ 369	1
+ 958	2
+ 358	6
+ 129	9
+ 849	3
+ 573	0
+ 906	5
+ 961	10
+ 646	5
+ 45	8
+ 59	4
+ 896	8
+ 259	1
+ 526	1
+ 904	1
+ 204	3
+ 162	2
+ 428	5
+ 793	6
+ 385	6
+ 849	10
+ 676	8
+ 440	6
+ 731	1
+ 94	8
+ 909	2
+ 166	8
+ 933	4
+ 923	5
+ 492	8
+ 531	7
+ 100	7
+ 858	5
+ 214	7
+ 86	6
+ 292	9
+ 556	10
+ 691	10
+ 604	4
+ 82	7
+ 197	10
+ 851	4
+ 796	8
+ 788	7
+ 243	3
+ 547	8
+ 975	6
+ 467	8
+ 176	7
+ 484	3
+ 279	8
+ 198	8
+ 743	9
+ 832	3
+ 310	9
+ 46	5
+ 906	9
+ 871	7
+ 681	7
+ 422	9
+ 938	10
+ 698	9
+ 615	2
+ 747	8
+ 846	2
+ 53	1
+ 6	3
+ 961	7
+ 139	8
+ 97	4
+ 707	1
+ 957	6
+ 40	8
+ 314	7
+ 487	7
+ 645	4
+ 704	3
+ 339	3
+ 508	1
+ 110	4
+ 315	2
+ 479	3
+ 414	4
+ 70	6
+ 231	2
+ 3	9
+ 311	10
+ 550	4
+ 788	9
+ 72	3
+ 600	7
+ 700	3
+ 60	0
+ 623	6
+ 124	7
+ 922	4
+ 897	4
+ 760	3
+ 839	8
+ 864	1
+ 998	9
+ 9	3
+ 827	6
+ 660	6
+ 423	7
+ 891	0
+ 450	6
+ 327	5
+ 630	10
+ 78	8
+ 685	0
+ 194	6
+ 401	10
+ 893	2
+ 785	8
+ 311	8
+ 625	3
+ 92	5
+ 878	8
+ 68	3
+ 484	10
+ 325	9
+ 550	7
+ 444	2
+ 603	5
+ 935	3
+ 522	1
+ 870	9
+ 82	8
+ 163	9
+ 521	5
+ 650	1
+ 794	7
+ 598	7
+ 494	7
+ 974	10
+ 625	3
+ 911	2
+ 951	4
+ 356	6
+ 877	3
+ 842	4
+ 419	7
+ 322	5
+ 476	5
+ 369	10
+ 960	0
+ 143	8
+ 761	7
+ 426	3
+ 408	4
+ 233	0
+ 698	1
+ 209	6
+ 499	6
+ 203	4
+ 856	0
+ 775	3
+ 757	1
+ 776	2
+ 583	1
+ 229	5
+ 164	4
+ 297	9
+ 114	7
+ 180	5
+ 122	4
+ 555	8
+ 556	8
+ 469	1
+ 328	7
+ 431	2
+ 717	2
+ 459	5
+ 302	2
+ 706	9
+ 380	9
+ 428	5
+ 308	7
+ 468	4
+ 447	6
+ 944	6
+ 60	5
+ 390	6
+ 262	9
+ 672	6
+ 531	1
+ 774	2
+ 307	2
+ 721	6
+ 468	4
+ 495	8
+ 363	9
+ 392	7
+ 648	9
+ 93	1
+ 508	0
+ 664	6
+ 536	1
+ 185	8
+ 913	9
+ 390	4
+ 959	2
+ 692	3
+ 397	4
+ 877	9
+ 841	4
+ 713	2
+ 295	1
+ 875	9
+ 965	10
+ 37	5
+ 6	7
+ 42	5
+ 755	2
+ 342	7
+ 84	7
+ 112	0
+ 895	8
+ 310	3
+ 218	2
+ 158	1
+ 559	9
+ 264	9
+ 976	1
+ 796	9
+ 108	8
+ 413	1
+ 533	5
+ 658	3
+ 682	10
+ 956	8
+ 731	1
+ 809	6
+ 873	1
+ 918	1
+ 307	1
+ 152	9
+ 947	4
+ 719	9
+ 555	5
+ 863	7
+ 347	3
+ 778	9
+ 731	4
+ 168	4
+ 435	1
+ 179	2
+ 193	10
+ 791	1
+ 108	7
+ 159	4
+ 786	3
+ 280	7
+ 726	10
+ 655	3
+ 512	5
+ 944	9
+ 793	7
+ 739	5
+ 158	9
+ 937	6
+ 32	1
+ 758	2
+ 104	5
+ 292	2
+ 259	5
+ 626	0
+ 761	9
+ 777	5
+ 903	4
+ 767	4
+ 949	7
+ 274	7
+ 434	0
+ 266	6
+ 921	2
+ 184	10
+ 318	9
+ 178	4
+ 491	5
+ 633	8
+ 921	3
+ 795	7
+ 164	6
+ 168	1
+ 3	9
+ 483	10
+ 647	8
+ 694	1
+ 771	10
+ 673	7
+ 163	9
+ 644	5
+ 799	8
+ 903	3
+ 292	5
+ 40	2
+ 794	8
+ 895	10
+ 407	1
+ 25	4
+ 999	5
+ 362	6
+ 265	1
+ 727	0
+ 16	4
+ 727	2
+ 257	4
+ 660	1
+ 193	6
+ 345	5
+ 98	4
+ 698	9
+ 221	6
+ 850	6
+ 656	9
+ 37	7
+ 383	4
+ 301	6
+ 455	0
+ 684	5
+ 428	4
+ 650	7
+ 781	3
+ 740	10
+ 872	1
+ 459	10
+ 471	2
+ 863	7
+ 749	7
+ 319	4
+ 589	4
+ 59	10
+ 755	4
+ 621	2
+ 388	3
+ 681	8
+ 716	3
+ 501	5
+ 641	2
+ 471	5
+ 327	9
+ 484	8
+ 87	3
+ 490	8
+ 60	8
+ 241	6
+ 166	3
+ 622	9
+ 661	2
+ 132	0
+ 547	8
+ 865	3
+ 144	4
+ 760	8
+ 606	2
+ 299	9
+ 162	8
+ 731	2
+ 130	2
+ 85	2
+ 30	3
+ 840	2
+ 627	5
+ 117	3
+ 705	2
+ 337	3
+ 61	2
+ 515	2
+ 566	3
+ 991	2
+ 506	3
+ 105	7
+ 74	8
+ 916	2
+ 57	0
+ 395	1
+ 328	2
+ 282	10
+ 697	4
+ 243	4
+ 647	6
+ 654	7
+ 781	2
+ 914	3
+ 444	9
+ 520	9
+ 196	6
+ 618	3
+ 461	5
+ 474	5
+ 535	9
+ 604	9
+ 105	9
+ 818	8
+ 285	1
+ 205	9
+ 641	9
+ 641	4
+ 28	6
+ 768	5
+ 461	3
+ 421	7
+ 913	0
+ 927	4
+ 573	4
+ 892	1
+ 271	5
+ 971	4
+ 382	8
+ 179	7
+ 851	4
+ 600	5
+ 243	2
+ 913	3
+ 796	7
+ 742	3
+ 969	2
+ 914	9
+ 202	8
+ 257	8
+ 243	1
+ 882	5
+ 644	9
+ 891	0
+ 643	1
+ 694	5
+ 454	3
+ 986	7
+ 535	9
+ 967	3
+ 580	7
+ 588	5
+ 871	5
+ 432	1
+ 344	7
+ 847	6
+ 837	7
+ 100	5
+ 583	10
+ 508	2
+ 61	2
+ 721	5
+ 497	7
+ 211	0
+ 606	2
+ 363	2
+ 887	10
+ 736	8
+ 454	2
+ 831	8
+ 858	7
+ 384	7
+ 408	5
+ 176	10
+ 475	7
+ 218	5
+ 887	9
+ 51	4
+ 646	3
+ 415	3
+ 440	8
+ 438	3
+ 729	2
+ 86	2
+ 344	9
+ 981	2
+ 596	4
+ 896	0
+ 850	1
+ 995	3
+ 756	2
+ 861	6
+ 152	9
+ 26	8
+ 174	4
+ 50	6
+ 218	5
+ 942	9
+ 663	0
+ 131	0
+ 944	1
+ 208	5
+ 477	1
+ 544	3
+ 176	5
+ 652	9
+ 752	5
+ 574	9
+ 424	6
+ 702	6
+ 41	8
+ 212	3
+ 241	2
+ 207	9
+ 181	3
+ 911	1
+ 450	1
+ 665	9
+ 222	2
+ 254	4
+ 748	9
+ 329	5
+ 418	9
+ 405	8
+ 504	1
+ 441	5
+ 860	7
+ 803	1
+ 807	0
+ 4	10
+ 348	9
+ 114	8
+ 33	8
+ 725	3
+ 988	10
+ 653	7
+ 885	10
+ 238	3
+ 886	6
+ 146	4
+ 751	6
+ 934	6
+ 240	7
+ 712	0
+ 748	7
+ 35	1
+ 631	1
+ 894	7
+ 928	6
+ 920	9
+ 598	6
+ 654	5
+ 556	9
+ 786	4
+ 535	9
+ 832	3
+ 518	8
+ 896	8
+ 504	6
+ 804	3
+ 324	8
+ 347	10
+ 989	2
+ 619	9
+ 860	5
+ 834	5
+ 113	5
+ 942	7
+ 380	7
+ 112	9
+ 659	9
+ 200	2
+ 711	1
+ 934	2
+ 704	7
+ 466	0
+ 578	8
+ 983	6
+ 55	6
+ 485	9
+ 142	3
+ 373	3
+ 808	3
+ 925	2
+ 43	0
+ 102	7
+ 981	3
+ 878	7
+ 398	8
+ 906	1
+ 551	4
+ 129	1
+ 186	1
+ 697	2
+ 715	2
+ 156	9
+ 502	5
+ 112	3
+ 844	0
+ 497	9
+ 74	6
+ 589	1
+ 900	5
+ 747	3
+ 280	7
+ 399	8
+ 26	5
+ 961	2
+ 641	7
+ 453	4
+ 840	6
+ 212	3
+ 138	3
+ 651	10
+ 362	1
+ 869	4
+ 746	5
+ 490	6
+ 925	2
+ 943	2
+ 890	3
+ 36	9
+ 870	10
+ 128	5
+ 655	6
+ 866	5
+ 190	1
+ 837	3
+ 403	5
+ 310	8
+ 636	2
+ 200	4
+ 637	7
+ 28	6
+ 927	10
+ 766	8
+ 313	8
+ 733	2
+ 798	9
+ 695	5
+ 443	6
+ 948	6
+ 640	8
+ 960	0
+ 274	3
+ 808	9
+ 449	0
+ 292	1
+ 698	3
+ 648	6
+ 291	4
+ 443	6
+ 215	2
+ 788	0
+ 37	5
+ 467	5
+ 44	4
+ 112	7
+ 200	1
+ 727	5
+ 342	5
+ 383	8
+ 542	7
+ 877	2
+ 995	5
+ 866	3
+ 938	3
+ 891	2
+ 484	7
+ 167	5
+ 162	6
+ 1	2
+ 48	1
+ 890	2
+ 186	6
+ 721	5
+ 151	1
+ 318	7
+ 779	2
+ 934	8
+ 719	8
+ 61	7
+ 108	10
+ 810	6
+ 632	10
+ 114	8
+ 610	1
+ 0	7
+ 229	9
+ 906	4
+ 506	6
+ 942	7
+ 731	3
+ 350	5
+ 455	3
+ 284	2
+ 83	3
+ 830	2
+ 297	6
+ 783	9
+ 617	9
+ 723	2
+ 12	7
+ 885	2
+ 614	8
+ 656	1
+ 418	6
+ 777	1
+ 858	1
+ 659	3
+ 411	9
+ 486	5
+ 288	3
+ 685	6
+ 957	5
+ 514	6
+ 365	2
+ 801	4
+ 961	7
+ 618	6
+ 477	3
+ 695	9
+ 871	5
+ 44	7
+ 600	7
+ 42	0
+ 646	5
+ 504	9
+ 845	2
+ 520	8
+ 657	0
+ 375	0
+ 272	2
+ 398	2
+ 862	0
+ 809	3
+ 290	5
+ 234	2
+ 976	3
+ 891	6
+ 982	9
+ 587	6
+ 461	1
+ 563	3
+ 280	1
+ 107	9
+ 117	5
+ 958	4
+ 658	4
+ 623	5
+ 372	4
+ 859	7
+ 935	1
+ 823	9
+ 372	7
+ 488	4
+ 646	1
+ 982	1
+ 165	5
+ 412	4
+ 628	5
+ 382	7
+ 2	3
+ 135	7
+ 696	8
+ 179	1
+ 190	0
+ 730	1
+ 131	6
+ 36	5
+ 266	5
+ 857	9
+ 598	8
+ 19	8
+ 384	4
+ 209	0
+ 951	6
+ 759	10
+ 932	9
+ 612	6
+ 652	8
+ 696	8
+ 830	4
+ 966	10
+ 978	0
+ 464	2
+ 527	3
+ 155	1
+ 160	2
+ 889	5
+ 605	1
+ 556	6
+ 690	3
+ 508	6
+ 209	1
+ 249	9
+ 912	9
+ 703	7
+ 370	7
+ 703	3
+ 672	2
+ 591	2
+ 488	7
+ 324	6
+ 921	2
+ 191	5
+ 311	7
+ 82	0
+ 62	6
+ 621	3
+ 710	9
+ 132	6
+ 815	8
+ 364	2
+ 504	1
+ 533	2
+ 234	1
+ 374	7
+ 872	7
+ 369	8
+ 911	6
+ 319	2
+ 307	4
+ 221	4
+ 990	8
+ 641	7
+ 713	8
+ 323	5
+ 608	7
+ 714	1
+ 755	2
+ 288	10
+ 372	7
+ 711	2
+ 360	1
+ 36	3
+ 640	4
+ 492	9
+ 755	7
+ 317	7
+ 556	10
+ 446	3
+ 731	8
+ 798	3
+ 457	5
+ 450	2
+ 760	7
+ 201	1
+ 400	9
+ 375	8
+ 991	4
+ 31	6
+ 765	5
+ 578	5
+ 237	9
+ 265	8
+ 852	7
+ 63	6
+ 481	9
+ 921	9
+ 374	4
+ 149	1
+ 109	3
+ 265	5
+ 261	6
+ 270	3
+ 50	3
+ 883	8
+ 824	5
+ 335	1
+ 355	6
+ 854	2
+ 312	9
+ 789	8
+ 778	7
+ 730	2
+ 81	9
+ 285	2
+ 228	6
+ 700	5
+ 190	10
+ 740	2
+ 271	6
+ 55	1
+ 84	4
+ 156	4
+ 990	0
+ 646	3
+ 927	4
+ 94	7
+ 145	8
+ 856	2
+ 702	1
+ 417	9
+ 692	1
+ 418	9
+ 87	2
+ 122	4
+ 782	2
+ 453	9
+ 567	6
+ 305	6
+ 619	10
+ 859	5
+ 386	10
+ 250	5
+ 776	1
+ 757	5
+ 249	2
+ 407	9
+ 291	8
+ 823	4
+ 983	9
+ 736	8
+ 121	2
+ 631	7
+ 798	9
+ 245	4
+ 886	1
+ 963	3
+ 57	2
+ 803	8
+ 320	6
+ 310	6
+ 734	7
+ 509	0
+ 543	3
+ 403	5
+ 276	1
+ 291	4
+ 328	9
+ 85	1
+ 858	3
+ 544	7
+ 434	5
+ 16	5
+ 719	8
+ 324	0
+ 378	6
+ 607	1
+ 352	1
+ 137	9
+ 447	5
+ 420	7
+ 679	7
+ 119	0
+ 634	2
+ 134	5
+ 535	7
+ 236	10
+ 184	3
+ 461	9
+ 71	8
+ 942	4
+ 418	5
+ 561	8
+ 664	7
+ 664	1
+ 238	1
+ 834	9
+ 796	10
+ 924	4
+ 158	1
+ 921	7
+ 735	2
+ 662	9
+ 409	1
+ 822	5
+ 907	8
+ 929	3
+ 312	5
+ 95	10
+ 188	8
+ 87	4
+ 844	9
+ 342	6
+ 874	3
+ 69	0
+ 324	10
+ 724	1
+ 148	4
+ 977	6
+ 510	8
+ 38	4
+ 563	10
+ 743	9
+ 458	8
+ 851	6
+ 598	9
+ 72	4
+ 859	4
+ 81	7
+ 680	2
+ 764	0
+ 141	5
+ 63	3
+ 875	0
+ 846	4
+ 839	9
+ 801	4
+ 851	5
+ 277	3
+ 382	1
+ 955	10
+ 65	0
+ 421	9
+ 441	5
+ 656	1
+ 653	4
+ 125	8
+ 908	2
+ 83	8
+ 228	9
+ 167	1
+ 813	10
+ 469	7
+ 513	7
+ 974	9
+ 873	9
+ 875	9
+ 955	3
+ 862	4
+ 799	5
+ 517	5
+ 939	6
+ 245	8
+ 830	3
+ 630	1
+ 257	8
+ 126	1
+ 765	6
+ 734	3
+ 341	7
+ 173	2
+ 637	0
+ 152	6
+ 344	0
+ 988	1
+ 533	5
+ 594	5
+ 148	8
+ 319	10
+ 166	9
+ 37	4
+ 745	2
+ 493	5
+ 757	2
+ 788	1
+ 935	10
+ 311	6
+ 9	5
+ 164	4
+ 478	2
+ 495	0
+ 658	1
+ 483	8
+ 927	8
+ 785	1
+ 751	8
+ 516	5
+ 984	0
+ 7	7
+ 235	8
+ 840	2
+ 756	2
+ 742	8
+ 615	9
+ 118	1
+ 58	6
+ 104	7
+ 700	6
+ 522	6
+ 389	3
+ 720	1
+ 128	2
+ 637	1
+ 244	6
+ 854	5
+ 439	7
+ 650	2
+ 845	4
+ 961	5
+ 298	1
+ 552	4
+ 690	7
+ 72	4
+ 243	6
+ 18	6
+ 901	7
+ 772	0
+ 973	4
+ 142	2
+ 52	10
+ 695	5
+ 691	3
+ 687	5
+ 737	6
+ 995	0
+ 725	5
+ 392	4
+ 203	5
+ 806	4
+ 59	8
+ 77	10
+ 562	8
+ 989	5
+ 258	1
+ 751	3
+ 127	4
+ 802	8
+ 792	5
+ 353	5
+ 136	3
+ 564	9
+ 895	10
+ 278	1
+ 420	1
+ 544	5
+ 908	6
+ 438	5
+ 471	4
+ 5	7
+ 558	8
+ 40	7
+ 203	8
+ 503	10
+ 331	9
+ 523	5
+ 205	1
+ 330	1
+ 42	6
+ 199	5
+ 692	7
+ 941	6
+ 363	4
+ 70	8
+ 806	1
+ 563	4
+ 831	6
+ 49	0
+ 445	6
+ 28	8
+ 408	6
+ 245	6
+ 638	6
+ 713	7
+ 182	9
+ 142	9
+ 654	1
+ 473	0
+ 463	5
+ 852	3
+ 619	4
+ 632	4
+ 18	7
+ 484	5
+ 233	5
+ 240	6
+ 63	5
+ 254	7
+ 59	10
+ 380	2
+ 880	5
+ 114	5
+ 606	6
+ 551	1
+ 131	4
+ 337	7
+ 818	10
+ 199	8
+ 650	7
+ 299	9
+ 195	5
+ 524	3
+ 23	8
+ 958	1
+ 746	3
+ 322	6
+ 860	4
+ 159	5
+ 23	7
+ 535	2
+ 114	9
+ 904	9
+ 842	1
+ 767	5
+ 786	1
+ 375	10
+ 604	9
+ 239	6
+ 678	2
+ 708	4
+ 534	0
+ 49	4
+ 466	2
+ 861	5
+ 919	4
+ 643	0
+ 269	5
+ 964	1
+ 650	7
+ 603	4
+ 797	10
+ 418	4
+ 877	7
+ 28	6
+ 853	7
+ 979	4
+ 766	0
+ 782	2
+ 236	6
+ 721	2
+ 40	4
+ 188	3
+ 911	2
+ 419	6
+ 884	0
+ 999	7
+ 1000	4
+ 82	9
+ 73	1
+ 432	9
+ 846	4
+ 313	6
+ 439	1
+ 844	7
+ 739	6
+ 831	8
+ 929	0
+ 87	8
+ 172	5
+ 402	1
+ 528	4
+ 736	5
+ 817	8
+ 405	9
+ 927	8
+ 817	8
+ 249	1
+ 384	7
+ 225	2
+ 364	10
+ 793	2
+ 742	7
+ 215	8
+ 562	4
+ 336	10
+ 443	9
+ 365	2
+ 392	2
+ 997	8
+ 72	9
+ 635	9
+ 697	9
+ 19	1
+ 573	2
+ 309	9
+ 208	1
+ 132	10
+ 824	3
+ 780	4
+ 734	1
+ 351	2
+ 980	7
+ 356	4
+ 897	4
+ 170	10
+ 276	8
+ 858	10
+ 690	9
+ 54	3
+ 121	4
+ 199	3
+ 466	3
+ 280	3
+ 678	1
+ 677	4
+ 175	0
+ 589	2
+ 743	9
+ 527	6
+ 297	7
+ 610	6
+ 502	5
+ 547	2
+ 345	6
+ 454	5
+ 965	7
+ 795	4
+ 983	1
+ 721	7
+ 135	4
+ 74	3
+ 425	7
+ 465	2
+ 607	10
+ 808	9
+ 689	4
+ 478	2
+ 886	0
+ 382	2
+ 626	8
+ 697	6
+ 488	5
+ 21	5
+ 567	7
+ 133	7
+ 140	2
+ 12	6
+ 869	5
+ 734	5
+ 469	5
+ 381	2
+ 960	9
+ 349	8
+ 884	7
+ 77	5
+ 567	8
+ 100	1
+ 266	1
+ 527	8
+ 864	7
+ 535	0
+ 867	5
+ 570	7
+ 24	3
+ 213	5
+ 845	6
+ 651	8
+ 453	0
+ 651	3
+ 732	7
+ 846	3
+ 501	9
+ 355	8
+ 67	9
+ 600	9
+ 542	1
+ 935	4
+ 682	5
+ 146	7
+ 808	4
+ 199	7
+ 953	9
+ 459	4
+ 851	1
+ 743	6
+ 837	6
+ 882	3
+ 534	2
+ 105	6
+ 118	7
+ 532	7
+ 840	5
+ 70	5
+ 971	2
+ 228	8
+ 575	4
+ 433	5
+ 277	9
+ 935	1
+ 1	7
+ 710	8
+ 266	6
+ 176	8
+ 828	3
+ 402	9
+ 986	9
+ 607	8
+ 399	7
+ 348	4
+ 892	6
+ 150	5
+ 1	6
+ 996	3
+ 474	9
+ 406	5
+ 609	1
+ 312	9
+ 708	5
+ 676	5
+ 768	1
+ 483	8
+ 10	1
+ 580	4
+ 766	9
+ 780	7
+ 502	9
+ 125	5
+ 513	1
+ 782	10
+ 52	2
+ 461	7
+ 304	8
+ 535	0
+ 261	2
+ 548	0
+ 288	0
+ 783	3
+ 121	4
+ 708	9
+ 290	5
+ 545	8
+ 418	7
+ 296	9
+ 791	1
+ 918	8
+ 266	4
+ 504	6
+ 153	0
+ 581	4
+ 250	1
+ 443	5
+ 161	2
+ 836	3
+ 589	5
+ 169	9
+ 32	7
+ 671	4
+ 384	10
+ 381	2
+ 45	3
+ 19	3
+ 678	5
+ 881	8
+ 561	5
+ 244	8
+ 591	7
+ 349	8
+ 913	2
+ 34	5
+ 728	2
+ 380	8
+ 916	1
+ 209	3
+ 18	6
+ 476	1
+ 890	5
+ 374	6
+ 17	3
+ 399	6
+ 716	6
+ 389	3
+ 330	7
+ 60	2
+ 922	1
+ 744	6
+ 296	1
+ 409	2
+ 174	6
+ 513	2
+ 209	10
+ 255	1
+ 483	6
+ 667	5
+ 883	1
+ 78	6
+ 708	5
+ 907	0
+ 204	10
+ 280	1
+ 60	0
+ 775	4
+ 147	2
+ 569	3
+ 803	1
+ 512	0
+ 71	8
+ 111	6
+ 396	8
+ 53	3
+ 842	1
+ 878	6
+ 597	8
+ 588	8
+ 751	9
+ 927	8
+ 891	7
+ 169	0
+ 886	7
+ 359	7
+ 820	9
+ 701	9
+ 638	8
+ 445	0
+ 588	5
+ 312	4
+ 628	2
+ 981	2
+ 975	6
+ 26	7
+ 437	10
+ 538	3
+ 655	7
+ 366	5
+ 445	7
+ 229	3
+ 595	9
+ 156	2
+ 741	6
+ 266	3
+ 98	6
+ 760	7
+ 768	7
+ 952	7
+ 310	10
+ 469	7
+ 931	0
+ 74	6
+ 713	4
+ 127	2
+ 164	4
+ 423	8
+ 286	6
+ 992	0
+ 180	3
+ 357	3
+ 837	1
+ 5	6
+ 858	10
+ 348	2
+ 935	8
+ 915	9
+ 823	10
+ 452	5
+ 429	6
+ 694	6
+ 935	1
+ 352	2
+ 696	3
+ 249	9
+ 602	6
+ 153	4
+ 722	2
+ 44	6
+ 115	4
+ 748	0
+ 208	7
+ 915	0
+ 652	4
+ 566	1
+ 946	3
+ 673	9
+ 377	0
+ 102	1
+ 369	4
+ 948	10
+ 956	1
+ 409	7
+ 259	5
+ 258	4
+ 844	0
+ 423	1
+ 669	3
+ 82	3
+ 705	6
+ 402	7
+ 908	1
+ 533	3
+ 101	6
+ 357	5
+ 986	3
+ 440	9
+ 406	8
+ 620	7
+ 303	9
+ 39	1
+ 885	5
+ 199	6
+ 801	3
+ 875	5
+ 929	3
+ 157	8
+ 353	7
+ 123	5
+ 325	5
+ 923	3
+ 785	4
+ 252	2
+ 213	9
+ 857	5
+ 751	9
+ 663	6
+ 359	9
+ 190	2
+ 142	1
+ 665	1
+ 343	8
+ 909	7
+ 513	0
+ 149	8
+ 513	1
+ 148	3
+ 435	4
+ 489	6
+ 273	3
+ 163	0
+ 243	8
+ 660	6
+ 687	8
+ 761	8
+ 914	4
+ 901	3
+ 249	8
+ 952	8
+ 843	1
+ 600	4
+ 173	7
+ 653	6
+ 149	1
+ 255	4
+ 489	4
+ 446	7
+ 244	1
+ 334	9
+ 955	1
+ 760	9
+ 521	7
+ 126	8
+ 471	1
+ 532	3
+ 180	1
+ 668	4
+ 880	3
+ 961	0
+ 464	2
+ 450	10
+ 634	9
+ 685	9
+ 2	0
+ 809	10
+ 113	6
+ 826	6
+ 230	10
+ 405	7
+ 30	9
+ 14	2
+ 69	7
+ 563	9
+ 3	5
+ 978	5
+ 740	4
+ 420	4
+ 324	1
+ 252	3
+ 123	1
+ 283	2
+ 631	1
+ 871	9
+ 60	3
+ 561	1
+ 213	6
+ 301	3
+ 257	9
+ 232	3
+ 388	2
+ 727	1
+ 637	1
+ 501	10
+ 252	8
+ 288	4
+ 815	6
+ 612	4
+ 678	5
+ 306	7
+ 759	9
+ 829	10
+ 442	1
+ 255	7
+ 994	5
+ 959	4
+ 696	7
+ 509	3
+ 833	0
+ 294	1
+ 764	6
+ 461	6
+ 152	1
+ 25	8
+ 555	3
+ 569	3
+ 199	4
+ 287	6
+ 528	5
+ 339	5
+ 28	3
+ 903	7
+ 983	4
+ 57	8
+ 422	4
+ 902	2
+ 933	4
+ 765	1
+ 435	8
+ 915	10
+ 122	5
+ 304	3
+ 882	6
+ 961	4
+ 133	3
+ 931	2
+ 598	8
+ 885	6
+ 246	9
+ 397	7
+ 292	3
+ 853	2
+ 662	6
+ 310	1
+ 409	2
+ 86	5
+ 709	4
+ 852	6
+ 982	8
+ 1	1
+ 114	9
+ 276	7
+ 766	2
+ 293	0
+ 102	7
+ 680	4
+ 989	5
+ 620	7
+ 152	9
+ 747	6
+ 154	8
+ 92	9
+ 224	9
+ 454	2
+ 758	5
+ 321	9
+ 386	6
+ 584	2
+ 758	9
+ 164	9
+ 567	8
+ 255	6
+ 377	9
+ 207	5
+ 804	10
+ 89	10
+ 788	2
+ 821	0
+ 126	3
+ 218	9
+ 729	5
+ 757	1
+ 136	3
+ 267	9
+ 219	4
+ 755	8
+ 275	0
+ 342	7
+ 885	5
+ 179	7
+ 503	3
+ 648	3
+ 450	5
+ 303	6
+ 743	5
+ 460	5
+ 60	2
+ 587	2
+ 559	9
+ 91	8
+ 285	8
+ 563	6
+ 856	9
+ 211	7
+ 454	4
+ 430	10
+ 659	1
+ 249	1
+ 546	6
+ 685	3
+ 72	1
+ 762	1
+ 363	3
+ 328	9
+ 202	4
+ 699	5
+ 265	3
+ 47	1
+ 168	3
+ 862	6
+ 649	3
+ 580	3
+ 369	8
+ 417	9
+ 379	1
+ 205	5
+ 247	10
+ 583	6
+ 315	9
+ 532	5
+ 331	2
+ 5	6
+ 493	1
+ 717	7
+ 310	6
+ 283	10
+ 870	9
+ 267	2
+ 691	7
+ 154	1
+ 786	4
+ 522	0
+ 326	1
+ 642	6
+ 17	2
+ 158	3
+ 405	2
+ 943	9
+ 215	7
+ 559	5
+ 238	8
+ 484	1
+ 704	8
+ 346	4
+ 435	5
+ 465	2
+ 860	10
+ 253	2
+ 92	9
+ 826	1
+ 70	10
+ 456	5
+ 147	4
+ 373	4
+ 60	9
+ 887	3
+ 774	4
+ 405	5
+ 122	8
+ 873	6
+ 253	3
+ 778	1
+ 326	0
+ 298	4
+ 927	1
+ 527	10
+ 109	10
+ 471	3
+ 383	8
+ 618	4
+ 775	5
+ 740	5
+ 875	1
+ 27	10
+ 897	9
+ 554	1
+ 239	3
+ 263	6
+ 362	6
+ 982	3
+ 686	5
+ 285	8
+ 492	8
+ 51	9
+ 600	7
+ 317	4
+ 173	1
+ 924	0
+ 203	10
+ 45	1
+ 851	6
+ 250	1
+ 930	5
+ 654	3
+ 74	6
+ 581	8
+ 145	9
+ 554	6
+ 623	6
+ 511	2
+ 274	8
+ 598	4
+ 886	5
+ 496	1
+ 474	5
+ 189	3
+ 141	4
+ 414	1
+ 953	1
+ 363	0
+ 704	9
+ 786	8
+ 811	3
+ 485	4
+ 946	10
+ 657	2
+ 825	3
+ 668	7
+ 778	2
+ 800	3
+ 705	10
+ 576	8
+ 429	10
+ 916	4
+ 58	3
+ 409	8
+ 225	2
+ 610	0
+ 536	1
+ 470	5
+ 92	1
+ 702	9
+ 383	4
+ 628	2
+ 533	4
+ 412	2
+ 417	10
+ 84	8
+ 978	0
+ 229	0
+ 280	6
+ 798	5
+ 834	4
+ 540	4
+ 504	0
+ 852	6
+ 138	6
+ 512	5
+ 925	1
+ 682	5
+ 567	1
+ 696	10
+ 82	8
+ 830	1
+ 780	1
+ 96	1
+ 697	9
+ 565	5
+ 302	1
+ 900	8
+ 116	8
+ 401	3
+ 307	9
+ 774	2
+ 52	5
+ 690	6
+ 550	4
+ 603	6
+ 166	4
+ 691	9
+ 493	8
+ 7	2
+ 681	6
+ 720	10
+ 677	6
+ 789	8
+ 374	2
+ 46	7
+ 103	8
+ 913	2
+ 276	6
+ 774	8
+ 989	4
+ 457	2
+ 811	1
+ 102	3
+ 935	1
+ 493	6
+ 680	2
+ 601	4
+ 835	4
+ 149	2
+ 580	2
+ 889	7
+ 14	8
+ 838	3
+ 404	6
+ 115	4
+ 989	6
+ 548	8
+ 720	6
+ 103	7
+ 758	6
+ 272	4
+ 810	9
+ 795	6
+ 262	9
+ 852	8
+ 138	7
+ 525	2
+ 543	4
+ 442	9
+ 975	6
+ 340	10
+ 129	9
+ 764	8
+ 537	9
+ 504	3
+ 463	8
+ 733	3
+ 649	5
+ 916	9
+ 471	8
+ 754	6
+ 510	3
+ 761	1
+ 642	2
+ 1000	6
+ 761	3
+ 581	9
+ 227	3
+ 740	8
+ 211	2
+ 58	7
+ 21	8
+ 946	7
+ 318	9
+ 582	8
+ 631	3
+ 398	1
+ 615	2
+ 194	3
+ 363	2
+ 875	1
+ 533	5
+ 16	8
+ 801	8
+ 522	0
+ 0	6
+ 686	0
+ 371	6
+ 692	7
+ 494	3
+ 478	1
+ 610	9
+ 266	2
+ 36	5
+ 483	4
+ 653	4
+ 524	2
+ 814	5
+ 945	6
+ 296	5
+ 627	3
+ 47	3
+ 318	4
+ 944	0
+ 108	4
+ 283	6
+ 564	9
+ 463	8
+ 118	5
+ 290	6
+ 898	9
+ 959	4
+ 129	8
+ 963	1
+ 388	3
+ 541	0
+ 555	6
+ 327	9
+ 6	3
+ 882	1
+ 711	2
+ 700	3
+ 58	2
+ 105	2
+ 662	4
+ 777	6
+ 338	7
+ 983	5
+ 509	9
+ 540	9
+ 205	1
+ 912	8
+ 669	2
+ 633	7
+ 511	5
+ 790	2
+ 680	5
+ 496	7
+ 653	6
+ 915	3
+ 995	7
+ 875	3
+ 429	9
+ 800	9
+ 804	3
+ 835	0
+ 422	7
+ 768	1
+ 987	4
+ 767	5
+ 915	6
+ 720	6
+ 47	2
+ 334	7
+ 817	2
+ 15	8
+ 941	9
+ 145	4
+ 747	9
+ 307	6
+ 286	1
+ 559	7
+ 890	3
+ 798	9
+ 727	6
+ 375	6
+ 122	1
+ 238	2
+ 311	6
+ 869	1
+ 820	9
+ 941	8
+ 773	1
+ 130	5
+ 31	4
+ 70	3
+ 580	6
+ 24	5
+ 956	8
+ 347	7
+ 387	7
+ 325	5
+ 817	6
+ 678	1
+ 134	5
+ 257	10
+ 431	2
+ 715	2
+ 284	8
+ 724	3
+ 281	8
+ 632	9
+ 423	7
+ 331	4
+ 477	7
+ 62	9
+ 400	4
+ 374	2
+ 950	1
+ 346	1
+ 599	6
+ 38	0
+ 800	8
+ 234	1
+ 597	10
+ 399	9
+ 751	0
+ 740	2
+ 686	1
+ 554	2
+ 749	6
+ 28	1
+ 2	4
+ 366	10
+ 454	7
+ 36	1
+ 314	1
+ 83	1
+ 827	3
+ 198	4
+ 275	6
+ 304	0
+ 628	0
+ 201	3
+ 114	8
+ 477	9
+ 370	5
+ 12	4
+ 907	4
+ 324	4
+ 89	4
+ 414	4
+ 435	5
+ 517	3
+ 815	7
+ 687	1
+ 313	10
+ 116	9
+ 34	3
+ 255	1
+ 71	7
+ 12	4
+ 237	0
+ 812	1
+ 401	1
+ 505	5
+ 496	9
+ 893	9
+ 417	4
+ 193	2
+ 125	9
+ 321	4
+ 871	4
+ 380	9
+ 753	6
+ 53	8
+ 366	1
+ 264	6
+ 88	1
+ 747	5
+ 213	3
+ 979	7
+ 171	9
+ 640	6
+ 281	8
+ 819	4
+ 714	1
+ 845	6
+ 577	2
+ 489	3
+ 859	5
+ 154	2
+ 607	4
+ 828	7
+ 495	6
+ 184	7
+ 827	2
+ 417	10
+ 34	1
+ 586	3
+ 890	4
+ 721	6
+ 545	6
+ 188	1
+ 791	7
+ 452	7
+ 219	6
+ 875	8
+ 25	7
+ 521	5
+ 279	7
+ 228	1
+ 868	6
+ 105	9
+ 701	7
+ 217	6
+ 96	9
+ 196	6
+ 505	4
+ 763	3
+ 61	2
+ 946	3
+ 823	8
+ 107	8
+ 525	6
+ 368	8
+ 333	6
+ 910	2
+ 240	0
+ 103	9
+ 706	3
+ 533	8
+ 258	7
+ 443	8
+ 112	2
+ 58	2
+ 423	0
+ 455	2
+ 825	6
+ 93	4
+ 190	5
+ 154	5
+ 56	1
+ 725	3
+ 79	8
+ 237	8
+ 147	8
+ 587	4
+ 498	0
+ 167	6
+ 236	2
+ 785	7
+ 230	2
+ 904	1
+ 801	10
+ 405	10
+ 458	6
+ 515	5
+ 623	2
+ 810	7
+ 67	0
+ 486	2
+ 817	1
+ 619	3
+ 102	8
+ 926	3
+ 11	7
+ 998	2
+ 950	9
+ 297	8
+ 899	7
+ 743	4
+ 261	3
+ 871	9
+ 498	7
+ 585	6
+ 728	1
+ 779	5
+ 144	4
+ 861	2
+ 183	8
+ 585	2
+ 498	6
+ 436	4
+ 485	7
+ 200	4
+ 434	9
+ 741	7
+ 202	6
+ 578	7
+ 293	2
+ 264	0
+ 234	0
+ 566	4
+ 440	4
+ 624	6
+ 213	2
+ 817	7
+ 791	3
+ 160	3
+ 984	4
+ 660	4
+ 303	4
+ 113	5
+ 13	7
+ 204	3
+ 855	5
+ 326	1
+ 511	9
+ 467	10
+ 318	1
+ 573	5
+ 300	4
+ 242	1
+ 642	4
+ 367	6
+ 762	0
+ 45	1
+ 428	2
+ 570	4
+ 851	8
+ 746	7
+ 243	1
+ 795	8
+ 963	3
+ 705	3
+ 354	3
+ 811	7
+ 668	1
+ 744	3
+ 456	1
+ 937	2
+ 137	10
+ 283	6
+ 140	9
+ 5	10
+ 628	8
+ 697	9
+ 823	5
+ 626	8
+ 755	3
+ 66	1
+ 609	9
+ 762	3
+ 931	5
+ 586	4
+ 616	5
+ 604	8
+ 505	9
+ 318	6
+ 741	3
+ 636	4
+ 74	3
+ 241	9
+ 825	9
+ 683	6
+ 197	7
+ 688	8
+ 626	5
+ 82	6
+ 956	7
+ 944	6
+ 192	5
+ 325	7
+ 436	6
+ 342	2
+ 965	10
+ 547	0
+ 312	8
+ 936	1
+ 654	6
+ 717	9
+ 368	4
+ 658	10
+ 855	7
+ 551	8
+ 409	5
+ 382	6
+ 44	7
+ 298	5
+ 349	6
+ 658	3
+ 619	2
+ 354	9
+ 992	3
+ 67	6
+ 909	8
+ 498	3
+ 188	2
+ 271	0
+ 895	8
+ 854	3
+ 318	2
+ 905	4
+ 943	2
+ 843	3
+ 843	5
+ 607	5
+ 705	10
+ 392	7
+ 251	5
+ 343	2
+ 242	8
+ 437	4
+ 995	7
+ 474	9
+ 530	3
+ 195	8
+ 565	1
+ 210	5
+ 303	1
+ 800	1
+ 553	4
+ 609	3
+ 368	0
+ 955	6
+ 460	3
+ 780	7
+ 138	2
+ 133	1
+ 926	6
+ 24	5
+ 935	2
+ 304	5
+ 318	5
+ 8	6
+ 568	8
+ 768	1
+ 216	4
+ 379	6
+ 377	3
+ 204	8
+ 631	10
+ 539	8
+ 202	7
+ 901	1
+ 279	9
+ 584	2
+ 143	9
+ 714	5
+ 403	7
+ 83	10
+ 530	9
+ 92	7
+ 228	5
+ 331	6
+ 804	5
+ 442	4
+ 520	10
+ 203	7
+ 652	1
+ 850	9
+ 29	4
+ 145	2
+ 323	9
+ 633	7
+ 581	7
+ 696	1
+ 567	8
+ 857	8
+ 259	2
+ 400	1
+ 723	8
+ 498	2
+ 822	7
+ 965	5
+ 804	8
+ 405	8
+ 249	6
+ 5	6
+ 409	6
+ 297	10
+ 354	10
+ 100	9
+ 782	10
+ 716	0
+ 146	1
+ 104	9
+ 958	6
+ 112	8
+ 302	1
+ 255	1
+ 892	7
+ 939	1
+ 211	9
+ 713	6
+ 582	0
+ 609	9
+ 4	7
+ 857	8
+ 667	6
+ 828	8
+ 690	9
+ 683	6
+ 533	8
+ 428	8
+ 873	7
+ 942	8
+ 344	9
+ 907	6
+ 825	6
+ 174	4
+ 630	8
+ 343	6
+ 492	2
+ 421	2
+ 774	2
+ 972	5
+ 180	7
+ 112	7
+ 450	5
+ 549	3
+ 224	5
+ 88	6
+ 372	10
+ 122	2
+ 614	3
+ 604	2
+ 77	9
+ 880	6
+ 148	3
+ 728	9
+ 550	7
+ 386	7
+ 354	5
+ 444	8
+ 38	10
+ 127	3
+ 484	2
+ 829	9
+ 209	10
+ 53	8
+ 245	7
+ 68	3
+ 605	9
+ 892	8
+ 249	6
+ 674	8
+ 319	1
+ 529	7
+ 558	10
+ 478	6
+ 967	6
+ 858	5
+ 820	7
+ 307	0
+ 637	4
+ 852	9
+ 19	9
+ 205	6
+ 869	1
+ 376	1
+ 717	1
+ 917	0
+ 111	4
+ 709	7
+ 420	2
+ 265	4
+ 792	1
+ 838	6
+ 809	1
+ 640	4
+ 506	5
+ 328	5
+ 414	5
+ 148	3
+ 630	5
+ 400	3
+ 576	3
+ 382	7
+ 764	1
+ 356	2
+ 279	6
+ 571	1
+ 743	4
+ 683	6
+ 554	3
+ 998	1
+ 816	3
+ 585	2
+ 858	7
+ 512	5
+ 258	9
+ 835	8
+ 230	2
+ 520	10
+ 308	9
+ 177	6
+ 497	7
+ 659	2
+ 157	3
+ 793	7
+ 665	8
+ 772	5
+ 116	4
+ 711	10
+ 90	2
+ 463	3
+ 136	3
+ 181	4
+ 514	7
+ 359	8
+ 577	5
+ 410	1
+ 285	1
+ 314	4
+ 411	1
+ 153	1
+ 897	9
+ 557	0
+ 281	3
+ 988	4
+ 492	5
+ 719	6
+ 748	9
+ 993	3
+ 601	4
+ 85	2
+ 889	5
+ 251	2
+ 564	6
+ 616	10
+ 672	8
+ 50	6
+ 694	6
+ 582	10
+ 875	6
+ 346	4
+ 21	1
+ 994	8
+ 964	10
+ 31	6
+ 340	1
+ 742	2
+ 610	10
+ 402	2
+ 559	0
+ 148	2
+ 786	2
+ 800	5
+ 805	4
+ 455	7
+ 952	8
+ 48	10
+ 866	0
+ 741	8
+ 29	8
+ 395	4
+ 887	1
+ 597	5
+ 132	10
+ 670	7
+ 17	8
+ 921	8
+ 17	7
+ 283	8
+ 103	7
+ 503	1
+ 541	6
+ 27	4
+ 592	8
+ 238	6
+ 539	6
+ 990	4
+ 771	6
+ 922	9
+ 586	6
+ 593	6
+ 411	5
+ 406	4
+ 235	7
+ 250	3
+ 428	8
+ 393	10
+ 303	4
+ 376	9
+ 188	6
+ 516	7
+ 246	5
+ 153	0
+ 93	1
+ 919	7
+ 667	5
+ 282	1
+ 27	7
+ 506	3
+ 378	8
+ 600	8
+ 509	10
+ 775	8
+ 414	2
+ 707	6
+ 765	2
+ 328	0
+ 729	5
+ 28	8
+ 556	9
+ 501	2
+ 460	8
+ 301	5
+ 471	8
+ 749	8
+ 563	3
+ 655	1
+ 342	4
+ 883	8
+ 582	6
+ 357	3
+ 813	7
+ 357	5
+ 166	4
+ 364	7
+ 333	9
+ 945	8
+ 649	2
+ 280	1
+ 53	0
+ 969	6
+ 377	6
+ 688	7
+ 55	6
+ 476	6
+ 161	8
+ 983	10
+ 519	3
+ 516	7
+ 726	9
+ 407	1
+ 745	4
+ 853	4
+ 598	1
+ 514	7
+ 161	5
+ 268	5
+ 107	10
+ 258	2
+ 527	7
+ 799	7
+ 567	8
+ 663	1
+ 123	2
+ 772	8
+ 59	2
+ 909	8
+ 532	8
+ 197	1
+ 894	7
+ 781	1
+ 193	0
+ 593	3
+ 5	9
+ 463	5
+ 585	3
+ 221	2
+ 45	9
+ 238	2
+ 63	0
+ 18	1
+ 189	9
+ 925	7
+ 688	1
+ 851	6
+ 833	6
+ 636	0
+ 681	2
+ 327	7
+ 80	8
+ 217	7
+ 53	4
+ 817	1
+ 322	1
+ 266	4
+ 66	3
+ 506	3
+ 210	4
+ 976	9
+ 554	8
+ 480	4
+ 458	1
+ 414	1
+ 345	7
+ 824	4
+ 532	0
+ 90	6
+ 480	9
+ 683	8
+ 963	9
+ 187	0
+ 234	7
+ 284	4
+ 124	3
+ 342	7
+ 87	8
+ 66	5
+ 938	5
+ 683	3
+ 221	5
+ 708	8
+ 548	8
+ 338	0
+ 706	0
+ 830	7
+ 971	0
+ 699	2
+ 709	10
+ 649	8
+ 244	10
+ 511	3
+ 813	6
+ 875	8
+ 57	6
+ 34	3
+ 66	7
+ 30	6
+ 541	4
+ 642	2
+ 390	5
+ 917	4
+ 487	6
+ 565	2
+ 600	2
+ 29	8
+ 205	5
+ 174	0
+ 118	0
+ 769	2
+ 608	8
+ 452	7
+ 545	5
+ 288	1
+ 851	9
+ 333	2
+ 401	3
+ 601	9
+ 867	2
+ 84	5
+ 380	1
+ 311	6
+ 654	5
+ 604	8
+ 535	4
+ 947	1
+ 176	4
+ 817	7
+ 881	1
+ 808	7
+ 34	1
+ 972	4
+ 392	6
+ 322	3
+ 740	4
+ 725	1
+ 520	0
+ 706	2
+ 521	3
+ 947	1
+ 683	9
+ 199	9
+ 292	0
+ 581	2
+ 120	4
+ 905	2
+ 529	9
+ 588	9
+ 450	9
+ 179	2
+ 318	9
+ 310	8
+ 941	0
+ 13	5
+ 325	10
+ 518	0
+ 853	7
+ 867	1
+ 732	4
+ 318	9
+ 836	2
+ 6	4
+ 100	6
+ 287	6
+ 505	5
+ 741	8
+ 370	1
+ 661	3
+ 67	7
+ 773	4
+ 633	3
+ 401	5
+ 7	3
+ 631	7
+ 716	9
+ 592	6
+ 173	6
+ 917	3
+ 192	2
+ 824	7
+ 670	6
+ 520	0
+ 616	2
+ 351	7
+ 854	1
+ 75	5
+ 414	5
+ 973	4
+ 744	6
+ 160	5
+ 554	8
+ 11	7
+ 349	9
+ 0	5
+ 132	8
+ 239	8
+ 389	8
+ 842	0
+ 941	2
+ 688	8
+ 317	8
+ 282	7
+ 239	3
+ 151	10
+ 860	3
+ 441	4
+ 62	5
+ 141	4
+ 381	1
+ 952	5
+ 965	2
+ 315	4
+ 950	2
+ 359	9
+ 351	0
+ 686	7
+ 809	10
+ 397	0
+ 224	5
+ 30	1
+ 859	5
+ 497	9
+ 924	6
+ 331	3
+ 779	3
+ 818	7
+ 474	1
+ 99	4
+ 291	5
+ 316	6
+ 504	0
+ 308	3
+ 970	7
+ 361	2
+ 254	4
+ 277	1
+ 863	8
+ 33	8
+ 413	4
+ 93	2
+ 648	9
+ 937	1
+ 44	0
+ 546	3
+ 493	9
+ 976	10
+ 863	3
+ 310	8
+ 991	7
+ 27	2
+ 63	3
+ 358	9
+ 78	4
+ 714	5
+ 755	8
+ 682	4
+ 717	6
+ 525	8
+ 654	1
+ 97	1
+ 933	1
+ 144	8
+ 358	5
+ 629	3
+ 126	7
+ 593	2
+ 959	10
+ 115	0
+ 342	8
+ 527	1
+ 635	2
+ 501	4
+ 828	0
+ 114	5
+ 96	2
+ 630	0
+ 284	8
+ 825	6
+ 228	5
+ 990	4
+ 109	6
+ 542	1
+ 534	7
+ 105	9
+ 485	6
+ 974	1
+ 842	5
+ 473	7
+ 500	6
+ 152	6
+ 798	8
+ 625	1
+ 555	4
+ 723	8
+ 903	7
+ 136	0
+ 296	7
+ 80	8
+ 334	2
+ 706	8
+ 818	7
+ 940	7
+ 154	4
+ 329	7
+ 1000	5
+ 249	8
+ 264	9
+ 879	8
+ 323	6
+ 602	2
+ 314	7
+ 239	6
+ 416	3
+ 439	7
+ 505	1
+ 569	3
+ 825	5
+ 982	10
+ 921	3
+ 633	9
+ 793	9
+ 718	1
+ 756	6
+ 877	1
+ 198	5
+ 306	5
+ 217	5
+ 121	6
+ 864	6
+ 382	4
+ 706	10
+ 691	5
+ 460	7
+ 511	4
+ 985	1
+ 302	8
+ 26	0
+ 835	8
+ 617	7
+ 862	8
+ 191	2
+ 326	4
+ 713	4
+ 41	6
+ 8	4
+ 946	7
+ 375	6
+ 245	8
+ 310	8
+ 216	3
+ 900	5
+ 73	9
+ 538	9
+ 708	2
+ 620	6
+ 970	8
+ 738	3
+ 219	5
+ 743	3
+ 28	8
+ 683	10
+ 465	1
+ 611	7
+ 893	9
+ 466	1
+ 215	4
+ 626	3
+ 291	2
+ 196	10
+ 319	8
+ 569	3
+ 627	3
+ 585	8
+ 758	3
+ 107	8
+ 80	8
+ 759	5
+ 848	4
+ 255	7
+ 291	7
+ 849	5
+ 87	5
+ 794	4
+ 640	10
+ 378	10
+ 807	9
+ 249	4
+ 253	8
+ 281	0
+ 162	4
+ 797	2
+ 177	6
+ 787	0
+ 926	0
+ 766	2
+ 763	6
+ 723	9
+ 92	5
+ 228	7
+ 507	6
+ 692	3
+ 552	9
+ 748	8
+ 775	0
+ 817	9
+ 417	6
+ 179	6
+ 170	10
+ 619	1
+ 7	4
+ 312	8
+ 1	0
+ 621	1
+ 552	8
+ 825	1
+ 455	5
+ 373	0
+ 457	1
+ 813	2
+ 151	6
+ 169	6
+ 243	3
+ 161	4
+ 314	8
+ 508	3
+ 166	8
+ 92	2
+ 856	7
+ 259	4
+ 561	1
+ 467	0
+ 600	8
+ 24	1
+ 961	8
+ 289	1
+ 467	5
+ 679	7
+ 806	8
+ 124	1
+ 621	6
+ 441	8
+ 453	5
+ 954	3
+ 245	2
+ 716	8
+ 297	2
+ 823	9
+ 22	8
+ 955	10
+ 684	2
+ 95	2
+ 702	8
+ 862	5
+ 615	10
+ 628	2
+ 617	1
+ 23	1
+ 602	10
+ 378	8
+ 189	1
+ 654	5
+ 276	5
+ 383	3
+ 323	3
+ 281	0
+ 582	4
+ 159	3
+ 151	0
+ 793	8
+ 6	4
+ 2	6
+ 491	0
+ 693	1
+ 2	1
+ 941	2
+ 164	6
+ 677	4
+ 71	1
+ 737	4
+ 398	0
+ 402	10
+ 395	6
+ 264	5
+ 581	1
+ 312	6
+ 477	3
+ 209	10
+ 340	9
+ 61	3
+ 972	0
+ 532	1
+ 596	2
+ 576	7
+ 269	3
+ 61	7
+ 331	5
+ 647	7
+ 23	9
+ 272	6
+ 967	6
+ 190	4
+ 899	4
+ 413	2
+ 300	5
+ 581	3
+ 475	1
+ 408	1
+ 323	10
+ 738	6
+ 297	8
+ 259	6
+ 262	9
+ 354	3
+ 817	6
+ 890	8
+ 211	1
+ 230	1
+ 479	6
+ 350	8
+ 116	9
+ 52	6
+ 44	5
+ 662	4
+ 443	4
+ 959	7
+ 200	2
+ 368	5
+ 124	7
+ 748	9
+ 349	6
+ 727	6
+ 718	10
+ 671	2
+ 599	0
+ 977	7
+ 952	0
+ 307	10
+ 488	10
+ 363	9
+ 369	3
+ 672	6
+ 540	0
+ 31	7
+ 763	8
+ 606	1
+ 417	3
+ 672	1
+ 289	3
+ 333	9
+ 364	3
+ 604	3
+ 339	9
+ 311	8
+ 879	7
+ 759	2
+ 996	4
+ 817	5
+ 471	8
+ 199	2
+ 627	8
+ 346	0
+ 138	0
+ 180	4
+ 362	5
+ 316	7
+ 823	9
+ 41	2
+ 830	4
+ 989	7
+ 26	7
+ 957	0
+ 179	8
+ 557	7
+ 622	8
+ 885	2
+ 562	2
+ 294	7
+ 250	5
+ 128	6
+ 987	4
+ 337	8
+ 363	4
+ 972	2
+ 730	10
+ 901	8
+ 710	9
+ 777	9
+ 632	3
+ 540	3
+ 91	4
+ 503	7
+ 656	8
+ 353	9
+ 271	5
+ 517	3
+ 924	9
+ 68	3
+ 232	0
+ 480	10
+ 2	4
+ 717	7
+ 240	5
+ 600	9
+ 829	1
+ 126	9
+ 564	6
+ 573	2
+ 426	9
+ 126	7
+ 406	6
+ 955	3
+ 498	0
+ 618	7
+ 62	1
+ 692	1
+ 481	4
+ 775	7
+ 904	4
+ 592	7
+ 515	7
+ 652	1
+ 347	2
+ 300	8
+ 150	4
+ 471	6
+ 69	4
+ 887	6
+ 448	5
+ 297	5
+ 605	10
+ 574	1
+ 398	3
+ 806	3
+ 725	4
+ 34	2
+ 116	7
+ 320	5
+ 911	6
+ 237	1
+ 46	7
+ 619	1
+ 133	5
+ 682	6
+ 12	10
+ 91	6
+ 967	7
+ 702	4
+ 14	5
+ 667	7
+ 905	7
+ 978	0
+ 387	3
+ 484	3
+ 918	7
+ 361	10
+ 429	10
+ 78	6
+ 485	8
+ 143	5
+ 738	2
+ 113	7
+ 899	8
+ 70	9
+ 322	7
+ 651	2
+ 438	6
+ 247	8
+ 927	7
+ 124	8
+ 453	5
+ 808	9
+ 464	9
+ 445	9
+ 646	6
+ 446	4
+ 822	6
+ 89	7
+ 374	2
+ 633	7
+ 897	3
+ 923	3
+ 913	2
+ 160	8
+ 902	3
+ 684	4
+ 768	5
+ 237	2
+ 378	7
+ 181	0
+ 270	6
+ 408	1
+ 187	5
+ 814	6
+ 657	4
+ 257	6
+ 731	2
+ 889	6
+ 350	0
+ 484	3
+ 333	2
+ 607	1
+ 661	8
+ 333	0
+ 527	5
+ 63	8
+ 142	5
+ 890	3
+ 968	7
+ 889	6
+ 151	1
+ 179	9
+ 325	1
+ 526	7
+ 116	0
+ 927	4
+ 178	5
+ 550	8
+ 379	9
+ 877	9
+ 398	9
+ 703	5
+ 410	6
+ 868	4
+ 297	8
+ 3	4
+ 903	2
+ 329	2
+ 250	9
+ 903	4
+ 865	8
+ 815	0
+ 366	4
+ 881	7
+ 248	8
+ 651	6
+ 698	4
+ 185	1
+ 947	1
+ 487	2
+ 810	5
+ 691	7
+ 672	0
+ 940	9
+ 875	8
+ 287	7
diff --git a/contrib/bloom/expected/bloom.out b/contrib/bloom/expected/bloom.out
new file mode 100644
index ...f7334fa
*** a/contrib/bloom/expected/bloom.out
--- b/contrib/bloom/expected/bloom.out
***************
*** 0 ****
--- 1,120 ----
+ CREATE EXTENSION bloom;
+ CREATE TABLE tst (
+ 	i	int4,
+ 	t	text
+ );
+ \copy tst from 'data/data'
+ CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+ SET enable_seqscan=on;
+ SET enable_bitmapscan=off;
+ SET enable_indexscan=off;
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16;
+                 QUERY PLAN                 
+ -------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: (i = 16)
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: (i = 16)
+ (5 rows)
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE t = '5';
+                 QUERY PLAN                 
+ -------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: (t = '5'::text)
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: (t = '5'::text)
+ (5 rows)
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+                         QUERY PLAN                        
+ ----------------------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: ((i = 16) AND (t = '5'::text))
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: ((i = 16) AND (t = '5'::text))
+ (5 rows)
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ VACUUM ANALYZE tst;
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ VACUUM FULL tst;
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ RESET enable_seqscan;
+ RESET enable_bitmapscan;
+ RESET enable_indexscan;
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
new file mode 100644
index ...e5c7a86
*** a/contrib/bloom/sql/bloom.sql
--- b/contrib/bloom/sql/bloom.sql
***************
*** 0 ****
--- 1,46 ----
+ CREATE EXTENSION bloom;
+ 
+ CREATE TABLE tst (
+ 	i	int4,
+ 	t	text
+ );
+ 
+ \copy tst from 'data/data'
+ 
+ CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+ 
+ SET enable_seqscan=on;
+ SET enable_bitmapscan=off;
+ SET enable_indexscan=off;
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16;
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE t = '5';
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ VACUUM ANALYZE tst;
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ VACUUM FULL tst;
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ RESET enable_seqscan;
+ RESET enable_bitmapscan;
+ RESET enable_indexscan;
#42Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Alexander Korotkov (#41)
Re: WIP: Access method extendability

Alexander Korotkov wrote:

Hi!

Patches was rebased against master.

In the attached version of patch access methods get support of pg_dump.

Thanks. This patch came in just as the commitfest was ending, so I'm
moving it to the next one.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#43Jim Nasby
Jim.Nasby@BlueTreble.com
In reply to: Alvaro Herrera (#42)
Re: WIP: Access method extendability

The following review has been posted through the commitfest application:
make installcheck-world: tested, passed
Implements feature: tested, failed
Spec compliant: not tested
Documentation: not tested

There are currently no docs or unit tests. I suspect this patch is still WIP?

create-am.5.patch:
General notes:
Needs catversion bump.

IndexQualInfo and GenericCosts have been moved to src/include/utils/selfuncs.h.

METHOD becomes an unreserved keyword.

generic-xlog.5.patch:
generic_xlog.c: At least needs a bunch of explanatory comments, if not info in a README. Since I don't really understand what the design here is my review is just cursory.

static memoryMove() - seems like an overly-generic function name to me...

writeCopyFlagment(), writeMoveFlagment(): s/Fl/Fr/?

bloom-control.5:
README:
+ CREATE INDEX bloomidx ON tbloom(i1,i2,i3) 
+        WITH (length=5, col1=2, col2=2, col3=4);
+ 
+ Here, we create bloom index with signature length 80 bits and attributes
+ i1, i2  mapped to 2 bits, attribute i3 - to 4 bits.

It's not clear to me where 80 bits is coming from...
bloom.h:
+ #define BITBYTE (8)
ISTR seeing this defined somewhere in the Postgres headers; dunno if it's worth using that definition instead.

Testing:
I ran the SELECT INTO from the README, as well as CREATE INDEX bloomidx. I then ran

insert into tbloom select
(generate_series(1,1000)*random())::int as i1,
(generate_series(1,1000)*random())::int as i2,
(generate_series(1,1000)*random())::int as i3,
(generate_series(1,1000)*random())::int as i4,
(generate_series(1,1000)*random())::int as i5,
(generate_series(1,1000)*random())::int as i6,
(generate_series(1,1000)*random())::int as i7,
(generate_series(1,1000)*random())::int as i8,
(generate_series(1,1000)*random())::int as i9,
(generate_series(1,1000)*random())::int as i10,
(generate_series(1,1000)*random())::int as i11,
(generate_series(1,1000)*random())::int as i12,
(generate_series(1,1000)*random())::int as i13
from generate_series(1,999);

and kill -9'd the backend. After restart I did VACUUM VERBOSE without issue. I ran the INSERT INTO, kill -9 and VACUUM VERBOSE sequence again. This time I got an assert:

TRAP: FailedAssertion("!(((bool) (((const void*)((ItemPointer) left) != ((void*)0)) && (((ItemPointer) left)->ip_posid != 0))))", File: "vacuumlazy.c", Line: 1823)

That line is

lblk = ItemPointerGetBlockNumber((ItemPointer) left);

which does

AssertMacro(ItemPointerIsValid(pointer)), \

which is the assert that's failing.

I've squirreled this install away for now, in case you can't repro this failure.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#44Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Jim Nasby (#43)
3 attachment(s)
Re: WIP: Access method extendability

Hi!

Next version of patch is attached.

On Tue, Feb 2, 2016 at 4:09 AM, Jim Nasby <Jim.Nasby@bluetreble.com> wrote:

The following review has been posted through the commitfest application:
make installcheck-world: tested, passed
Implements feature: tested, failed
Spec compliant: not tested
Documentation: not tested

There are currently no docs or unit tests. I suspect this patch is still
WIP?

I hope to exit WIP state soon...

create-am.5.patch:

General notes:
Needs catversion bump.

Yes. I think catversion bump is committer responsibility and shouldn't be
included into patch.

IndexQualInfo and GenericCosts have been moved to
src/include/utils/selfuncs.h.

Yes, they have been moved in order to be accessed by custom index access
method.

METHOD becomes an unreserved keyword.

Seems to be inevitable if we want CREATE ACCESS METHOD command.

generic-xlog.5.patch:
generic_xlog.c: At least needs a bunch of explanatory comments, if not
info in a README. Since I don't really understand what the design here is
my review is just cursory.

Make detailed explanation of API in generic_xlog.h. Could be moved into
README if needed.

static memoryMove() - seems like an overly-generic function name to me...

I've simplified generic xlog to just per byte comparison without support of
memory move. Now it would be much easier to commit. Support of memory move
could be added in the separate patch though.

writeCopyFlagment(), writeMoveFlagment(): s/Fl/Fr/?

Fixed.

bloom-control.5:

README:
+ CREATE INDEX bloomidx ON tbloom(i1,i2,i3)
+        WITH (length=5, col1=2, col2=2, col3=4);
+
+ Here, we create bloom index with signature length 80 bits and attributes
+ i1, i2  mapped to 2 bits, attribute i3 - to 4 bits.

It's not clear to me where 80 bits is coming from...

length is measure in uint16...
For now, it's documented.

bloom.h:
+ #define BITBYTE (8)
ISTR seeing this defined somewhere in the Postgres headers; dunno if it's
worth using that definition instead.

Got rid of this. Using BITS_PER_BYTE now.

Testing:
I ran the SELECT INTO from the README, as well as CREATE INDEX bloomidx. I
then ran

insert into tbloom select
(generate_series(1,1000)*random())::int as i1,
(generate_series(1,1000)*random())::int as i2,
(generate_series(1,1000)*random())::int as i3,
(generate_series(1,1000)*random())::int as i4,
(generate_series(1,1000)*random())::int as i5,
(generate_series(1,1000)*random())::int as i6,
(generate_series(1,1000)*random())::int as i7,
(generate_series(1,1000)*random())::int as i8,
(generate_series(1,1000)*random())::int as i9,
(generate_series(1,1000)*random())::int as i10,
(generate_series(1,1000)*random())::int as i11,
(generate_series(1,1000)*random())::int as i12,
(generate_series(1,1000)*random())::int as i13
from generate_series(1,999);

and kill -9'd the backend. After restart I did VACUUM VERBOSE without
issue. I ran the INSERT INTO, kill -9 and VACUUM VERBOSE sequence again.
This time I got an assert:

TRAP: FailedAssertion("!(((bool) (((const void*)((ItemPointer) left) !=
((void*)0)) && (((ItemPointer) left)->ip_posid != 0))))", File:
"vacuumlazy.c", Line: 1823)

That line is

lblk = ItemPointerGetBlockNumber((ItemPointer) left);

which does

AssertMacro(ItemPointerIsValid(pointer)), \

which is the assert that's failing.

I've squirreled this install away for now, in case you can't repro this
failure.

Should be fixed.

General notes about current version of patch:
* A lot of comments added.
* bloom documentation is moved from README to SGML with a lot of addons and
cleanup.
* Memory move support in generic xlog is removed. Now it's much more simple
and clean.
* Tests for CREATE ACCESS METHOD added. For now, it creates a mirror of
GiST access method.
* Syntax for CREATE ACCESS METHOD is changed. For now, it's "CREATE ACCESS
METHOD amname TYPE INDEX HANDLER handler;" in respect of parallel work on
sequential access methods. New access method attribute added: amtype.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

Attachments:

create-am.6.patchapplication/octet-stream; name=create-am.6.patchDownload
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
new file mode 100644
index c48e37b..a86a488
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 20,25 ****
--- 20,26 ----
  #include "catalog/heap.h"
  #include "catalog/index.h"
  #include "catalog/objectaccess.h"
+ #include "catalog/pg_am.h"
  #include "catalog/pg_amop.h"
  #include "catalog/pg_amproc.h"
  #include "catalog/pg_attrdef.h"
*************** static const Oid object_classes[] = {
*** 160,166 ****
  	ExtensionRelationId,		/* OCLASS_EXTENSION */
  	EventTriggerRelationId,		/* OCLASS_EVENT_TRIGGER */
  	PolicyRelationId,			/* OCLASS_POLICY */
! 	TransformRelationId			/* OCLASS_TRANSFORM */
  };
  
  
--- 161,168 ----
  	ExtensionRelationId,		/* OCLASS_EXTENSION */
  	EventTriggerRelationId,		/* OCLASS_EVENT_TRIGGER */
  	PolicyRelationId,			/* OCLASS_POLICY */
! 	TransformRelationId,		/* OCLASS_TRANSFORM */
! 	AccessMethodRelationId		/* OCLASS_AM */
  };
  
  
*************** doDeletion(const ObjectAddress *object, 
*** 1270,1275 ****
--- 1272,1280 ----
  
  		case OCLASS_TRANSFORM:
  			DropTransformById(object->objectId);
+ 
+ 		case OCLASS_AM:
+ 			RemoveAccessMethodById(object->objectId);
  			break;
  
  		default:
*************** getObjectClass(const ObjectAddress *obje
*** 2415,2420 ****
--- 2420,2428 ----
  
  		case TransformRelationId:
  			return OCLASS_TRANSFORM;
+ 
+ 		case AccessMethodRelationId:
+ 			return OCLASS_AM;
  	}
  
  	/* shouldn't get here */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
new file mode 100644
index d2aaa6d..83e8b24
*** a/src/backend/catalog/objectaddress.c
--- b/src/backend/catalog/objectaddress.c
*************** static const ObjectPropertyType ObjectPr
*** 438,443 ****
--- 438,455 ----
  		Anum_pg_type_typacl,
  		ACL_KIND_TYPE,
  		true
+ 	},
+ 	{
+ 		AccessMethodRelationId,
+ 		AmOidIndexId,
+ 		AMOID,
+ 		AMNAME,
+ 		Anum_pg_am_amname,
+ 		InvalidAttrNumber,
+ 		InvalidAttrNumber,
+ 		InvalidAttrNumber,
+ 		-1,
+ 		true
  	}
  };
  
*************** static ObjectAddress get_object_address_
*** 674,679 ****
--- 686,693 ----
  							   List *objargs, bool missing_ok);
  static ObjectAddress get_object_address_defacl(List *objname, List *objargs,
  						  bool missing_ok);
+ static ObjectAddress get_object_address_am(ObjectType objtype, List *objname,
+ 						bool missing_ok);
  static const ObjectPropertyType *get_object_property_data(Oid class_id);
  
  static void getRelationDescription(StringInfo buffer, Oid relid);
*************** get_object_address(ObjectType objtype, L
*** 913,918 ****
--- 927,935 ----
  				address = get_object_address_defacl(objname, objargs,
  													missing_ok);
  				break;
+ 			case OBJECT_ACCESS_METHOD:
+ 				address = get_object_address_am(objtype, objname, missing_ok);
+ 				break;
  			default:
  				elog(ERROR, "unrecognized objtype: %d", (int) objtype);
  				/* placate compiler, in case it thinks elog might return */
*************** get_object_address_opcf(ObjectType objty
*** 1489,1495 ****
  	ObjectAddress address;
  
  	/* XXX no missing_ok support here */
! 	amoid = get_am_oid(strVal(linitial(objname)), false);
  	objname = list_copy_tail(objname, 1);
  
  	switch (objtype)
--- 1506,1512 ----
  	ObjectAddress address;
  
  	/* XXX no missing_ok support here */
! 	amoid = get_am_oid(strVal(linitial(objname)), 'i', false);
  	objname = list_copy_tail(objname, 1);
  
  	switch (objtype)
*************** get_object_address_opcf(ObjectType objty
*** 1516,1521 ****
--- 1533,1597 ----
  }
  
  /*
+  * Find the ObjectAddress for an access method.
+  */
+ static ObjectAddress
+ get_object_address_am(ObjectType objtype, List *objname, bool missing_ok)
+ {
+ 	ObjectAddress	address;
+ 	char		   *amname, *catalogname;
+ 	Type		tup;
+ 
+ 	switch (list_length(objname))
+ 	{
+ 		case 1:
+ 			amname = strVal(linitial(objname));
+ 			break;
+ 		case 2:
+ 			catalogname = strVal(linitial(objname));
+ 			amname = strVal(lsecond(objname));
+ 
+ 			/*
+ 			 * We check the catalog name and then ignore it.
+ 			 */
+ 			if (strcmp(catalogname, get_database_name(MyDatabaseId)) != 0)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				  errmsg("cross-database references are not implemented: %s",
+ 						 NameListToString(objname))));
+ 			break;
+ 		default:
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_SYNTAX_ERROR),
+ 				errmsg("improper access method name (too many dotted names): %s",
+ 					   NameListToString(objname))));
+ 			break;
+ 	}
+ 
+ 	address.classId = AccessMethodRelationId;
+ 	address.objectId = InvalidOid;
+ 	address.objectSubId = 0;
+ 
+ 	tup = SearchSysCache1(AMNAME, PointerGetDatum(amname));
+ 	if (!HeapTupleIsValid(tup))
+ 	{
+ 		/* Access method is missing, report error if needed */
+ 		if (!missing_ok)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 					 errmsg("access method \"%s\" does not exist",
+ 							amname)));
+ 		return address;
+ 	}
+ 
+ 	/* Access method is found, return its oid */
+ 	address.objectId = HeapTupleGetOid(tup);
+ 	ReleaseSysCache(tup);
+ 
+ 	return address;
+ }
+ 
+ /*
   * Find the ObjectAddress for an opclass/opfamily member.
   *
   * (The returned address corresponds to a pg_amop/pg_amproc object).
*************** check_object_ownership(Oid roleid, Objec
*** 2179,2184 ****
--- 2255,2261 ----
  			break;
  		case OBJECT_TSPARSER:
  		case OBJECT_TSTEMPLATE:
+ 		case OBJECT_ACCESS_METHOD:
  			/* We treat these object types as being owned by superusers */
  			if (!superuser_arg(roleid))
  				ereport(ERROR,
*************** getObjectDescription(const ObjectAddress
*** 3129,3134 ****
--- 3206,3226 ----
  				break;
  			}
  
+ 		case OCLASS_AM:
+ 			{
+ 				HeapTuple	tup;
+ 
+ 				tup = SearchSysCache1(AMOID,
+ 									  ObjectIdGetDatum(object->objectId));
+ 				if (!HeapTupleIsValid(tup))
+ 					elog(ERROR, "cache lookup failed for access method %u",
+ 						 object->objectId);
+ 				appendStringInfo(&buffer, _("access method %s"),
+ 							NameStr(((Form_pg_am) GETSTRUCT(tup))->amname));
+ 				ReleaseSysCache(tup);
+ 				break;
+ 			}
+ 
  		default:
  			appendStringInfo(&buffer, "unrecognized object %u %u %d",
  							 object->classId,
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
new file mode 100644
index b1ac704..6b3742c
*** a/src/backend/commands/Makefile
--- b/src/backend/commands/Makefile
*************** subdir = src/backend/commands
*** 12,18 ****
  top_builddir = ../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o  \
  	collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
  	dbcommands.o define.o discard.o dropcmds.o \
  	event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
--- 12,18 ----
  top_builddir = ../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
  	collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
  	dbcommands.o define.o discard.o dropcmds.o \
  	event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
new file mode 100644
index ...256d5e2
*** a/src/backend/commands/amcmds.c
--- b/src/backend/commands/amcmds.c
***************
*** 0 ****
--- 1,289 ----
+ /*-------------------------------------------------------------------------
+  *
+  * amcmds.c
+  *	  Routines for SQL commands that manipulate access methods.
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *
+  * IDENTIFICATION
+  *	  src/backend/commands/amcmds.c
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "access/heapam.h"
+ #include "access/htup_details.h"
+ #include "access/xact.h"
+ #include "catalog/binary_upgrade.h"
+ #include "catalog/catalog.h"
+ #include "catalog/dependency.h"
+ #include "catalog/heap.h"
+ #include "catalog/indexing.h"
+ #include "catalog/objectaccess.h"
+ #include "catalog/pg_authid.h"
+ #include "catalog/pg_am.h"
+ #include "catalog/pg_collation.h"
+ #include "catalog/pg_constraint.h"
+ #include "catalog/pg_depend.h"
+ #include "catalog/pg_enum.h"
+ #include "catalog/pg_language.h"
+ #include "catalog/pg_namespace.h"
+ #include "catalog/pg_proc.h"
+ #include "catalog/pg_proc_fn.h"
+ #include "catalog/pg_range.h"
+ #include "catalog/pg_type.h"
+ #include "catalog/pg_type_fn.h"
+ #include "commands/dbcommands.h"
+ #include "commands/defrem.h"
+ #include "commands/tablecmds.h"
+ #include "commands/typecmds.h"
+ #include "executor/executor.h"
+ #include "miscadmin.h"
+ #include "nodes/makefuncs.h"
+ #include "optimizer/planner.h"
+ #include "optimizer/var.h"
+ #include "parser/parse_coerce.h"
+ #include "parser/parse_collate.h"
+ #include "parser/parse_expr.h"
+ #include "parser/parse_func.h"
+ #include "parser/parse_type.h"
+ #include "utils/acl.h"
+ #include "utils/builtins.h"
+ #include "utils/fmgroids.h"
+ #include "utils/lsyscache.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ #include "utils/snapmgr.h"
+ #include "utils/syscache.h"
+ #include "utils/tqual.h"
+ 
+ /*
+  * Convert a handler function name passed from the parser to an Oid.
+  */
+ static Oid
+ lookup_index_am_handler_func(List *handler_name)
+ {
+ 	Oid			handlerOid;
+ 	Oid			funcargtypes[1] = {INTERNALOID};
+ 
+ 	if (handler_name == NIL)
+ 		return InvalidOid;
+ 
+ 	/* handlers have no arguments */
+ 	handlerOid = LookupFuncName(handler_name, 1, funcargtypes, false);
+ 
+ 	/* check that handler has correct return type */
+ 	if (get_func_rettype(handlerOid) != INDEX_AM_HANDLEROID)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 				 errmsg("function %s must return type \"index_am_handler\"",
+ 						NameListToString(handler_name))));
+ 
+ 	return handlerOid;
+ }
+ 
+ 
+ /*
+  * CreateAcessMethod
+  *		Registers a new access method.
+  */
+ ObjectAddress
+ CreateAccessMethod(CreateAmStmt *stmt)
+ {
+ 	Relation		rel;
+ 	ObjectAddress	myself;
+ 	ObjectAddress	referenced;
+ 	Oid				amoid;
+ 	Oid				amhandler;
+ 	bool			nulls[Natts_pg_am];
+ 	Datum			values[Natts_pg_am];
+ 	HeapTuple		tup;
+ 
+ 	rel = heap_open(AccessMethodRelationId, RowExclusiveLock);
+ 
+ 	/* Must be super user */
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 			errmsg("permission denied to create access method \"%s\"",
+ 				   stmt->amname),
+ 			errhint("Must be superuser to create access method.")));
+ 
+ 	/* Check if name is busy */
+ 	amoid = GetSysCacheOid1(AMNAME, CStringGetDatum(stmt->amname));
+ 	if (OidIsValid(amoid))
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_DUPLICATE_OBJECT),
+ 				 errmsg("access method \"%s\" already exists", stmt->amname)));
+ 	}
+ 
+ 	/*
+ 	 * Get handler function oid. Handler signature depends on access method
+ 	 * type.
+ 	 */
+ 	switch(stmt->amtype)
+ 	{
+ 		case 'i':
+ 			amhandler = lookup_index_am_handler_func(stmt->handler_name);
+ 			if (!OidIsValid(amhandler))
+ 			{
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_UNDEFINED_FUNCTION),
+ 						 errmsg("handler function \"%s\" is undefined",
+ 								NameListToString(stmt->handler_name))));
+ 			}
+ 			break;
+ 		default:
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 					 errmsg("wrong access method type \"%c\"", stmt->amtype)));
+ 			break;
+ 	}
+ 
+ 	/*
+ 	 * Insert tuple into pg_am.
+ 	 */
+ 	memset(values, 0, sizeof(values));
+ 	memset(nulls, false, sizeof(nulls));
+ 
+ 	values[Anum_pg_am_amname - 1] =
+ 		DirectFunctionCall1(namein, CStringGetDatum(stmt->amname));
+ 	values[Anum_pg_am_amhandler - 1] = ObjectIdGetDatum(amhandler);
+ 	values[Anum_pg_am_amtype - 1] = CharGetDatum(stmt->amtype);
+ 
+ 	tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+ 
+ 	amoid = simple_heap_insert(rel, tup);
+ 	CatalogUpdateIndexes(rel, tup);
+ 	heap_freetuple(tup);
+ 
+ 	myself.classId = AccessMethodRelationId;
+ 	myself.objectId = amoid;
+ 	myself.objectSubId = 0;
+ 
+ 	/* Record dependecy on handler function */
+ 	referenced.classId = ProcedureRelationId;
+ 	referenced.objectId = amhandler;
+ 	referenced.objectSubId = 0;
+ 
+ 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ 
+ 	recordDependencyOnCurrentExtension(&myself, false);
+ 
+ 	heap_close(rel, RowExclusiveLock);
+ 
+ 	return myself;
+ }
+ 
+ /*
+  * Guts of access method deletion.
+  */
+ void
+ RemoveAccessMethodById(Oid amOid)
+ {
+ 	Relation	relation;
+ 	HeapTuple	tup;
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 errmsg("must be superuser to drop access methods")));
+ 
+ 	relation = heap_open(AccessMethodRelationId, RowExclusiveLock);
+ 
+ 	tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
+ 	if (!HeapTupleIsValid(tup))
+ 		elog(ERROR, "cache lookup failed for access method %u", amOid);
+ 
+ 	simple_heap_delete(relation, &tup->t_self);
+ 
+ 	ReleaseSysCache(tup);
+ 
+ 	heap_close(relation, RowExclusiveLock);
+ }
+ 
+ /*
+  * Convert single charater access method type into string for error reporting.
+  */
+ static char *
+ get_am_type_string(char amtype)
+ {
+ 	switch (amtype)
+ 	{
+ 		case 'i':
+ 			return "index";
+ 		default:
+ 			elog(ERROR, "invalid access method type '%c'", amtype);
+ 	}
+ }
+ 
+ /*
+  * get_am_oid - given an access method name and type, look up the OID
+  *
+  * If missing_ok is false, throw an error if access method not found.  If
+  * true, just return InvalidOid.
+  */
+ Oid
+ get_am_oid(const char *amname, char amtype, bool missing_ok)
+ {
+ 	HeapTuple	tup;
+ 	Oid			oid = InvalidOid;
+ 
+ 	tup = SearchSysCache1(AMNAME, CStringGetDatum(amname));
+ 	if (HeapTupleIsValid(tup))
+ 	{
+ 		Form_pg_am amform = (Form_pg_am) GETSTRUCT(tup);
+ 
+ 		if (amform->amtype != amtype)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 					 errmsg("access method \"%s\" type is not %s",
+ 							NameStr(amform->amname),
+ 							get_am_type_string(amtype))));
+ 
+ 		oid = HeapTupleGetOid(tup);
+ 		ReleaseSysCache(tup);
+ 	}
+ 
+ 	if (!OidIsValid(oid) && !missing_ok)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 				 errmsg("access method \"%s\" does not exist", amname)));
+ 	return oid;
+ }
+ 
+ /*
+  * get_am_oid - given an access method OID name and type, look up the name
+  *
+  * Access method type is not required for lookup.  However it's useful to check
+  * the type to ensure it is what we're looking for.
+  */
+ char *
+ get_am_name(Oid amOid, char amtype)
+ {
+ 	HeapTuple	tup;
+ 	char	   *result = NULL;
+ 
+ 	tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
+ 	if (HeapTupleIsValid(tup))
+ 	{
+ 		Form_pg_am amform = (Form_pg_am) GETSTRUCT(tup);
+ 
+ 		if (amform->amtype != amtype)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 					 errmsg("access method \"%s\" type is not %s",
+ 							NameStr(amform->amname),
+ 							get_am_type_string(amtype))));
+ 
+ 
+ 		result = pstrdup(NameStr(amform->amname));
+ 		ReleaseSysCache(tup);
+ 	}
+ 	return result;
+ }
+ 
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
new file mode 100644
index 9e32f8d..3f52ad8
*** a/src/backend/commands/event_trigger.c
--- b/src/backend/commands/event_trigger.c
*************** typedef enum
*** 86,91 ****
--- 86,92 ----
  
  /* XXX merge this with ObjectTypeMap? */
  static event_trigger_support_data event_trigger_support[] = {
+ 	{"ACCESS METHOD", true},
  	{"AGGREGATE", true},
  	{"CAST", true},
  	{"CONSTRAINT", true},
*************** EventTriggerSupportsObjectType(ObjectTyp
*** 1078,1083 ****
--- 1079,1085 ----
  		case OBJECT_EVENT_TRIGGER:
  			/* no support for event triggers on event triggers */
  			return false;
+ 		case OBJECT_ACCESS_METHOD:
  		case OBJECT_AGGREGATE:
  		case OBJECT_AMOP:
  		case OBJECT_AMPROC:
*************** EventTriggerSupportsObjectClass(ObjectCl
*** 1167,1172 ****
--- 1169,1175 ----
  		case OCLASS_DEFACL:
  		case OCLASS_EXTENSION:
  		case OCLASS_POLICY:
+ 		case OCLASS_AM:
  			return true;
  	}
  
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
new file mode 100644
index 8a66196..a15f22f
*** a/src/backend/commands/opclasscmds.c
--- b/src/backend/commands/opclasscmds.c
*************** DefineOpClass(CreateOpClassStmt *stmt)
*** 678,683 ****
--- 678,689 ----
  	myself.objectId = opclassoid;
  	myself.objectSubId = 0;
  
+ 	/* dependency on access method */
+ 	referenced.classId = AccessMethodRelationId;
+ 	referenced.objectId = amoid;
+ 	referenced.objectSubId = 0;
+ 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ 
  	/* dependency on namespace */
  	referenced.classId = NamespaceRelationId;
  	referenced.objectId = namespaceoid;
*************** DefineOpFamily(CreateOpFamilyStmt *stmt)
*** 743,749 ****
  					   get_namespace_name(namespaceoid));
  
  	/* Get access method OID, throwing an error if it doesn't exist. */
! 	amoid = get_am_oid(stmt->amname, false);
  
  	/* XXX Should we make any privilege check against the AM? */
  
--- 749,755 ----
  					   get_namespace_name(namespaceoid));
  
  	/* Get access method OID, throwing an error if it doesn't exist. */
! 	amoid = get_am_oid(stmt->amname, 'i', false);
  
  	/* XXX Should we make any privilege check against the AM? */
  
*************** assignOperTypes(OpFamilyMember *member, 
*** 1106,1112 ****
  			ereport(ERROR,
  					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
  			errmsg("access method \"%s\" does not support ordering operators",
! 				   get_am_name(amoid))));
  	}
  	else
  	{
--- 1112,1118 ----
  			ereport(ERROR,
  					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
  			errmsg("access method \"%s\" does not support ordering operators",
! 				   get_am_name(amoid, 'i'))));
  	}
  	else
  	{
*************** RemoveAmProcEntryById(Oid entryOid)
*** 1663,1683 ****
  	heap_close(rel, RowExclusiveLock);
  }
  
- char *
- get_am_name(Oid amOid)
- {
- 	HeapTuple	tup;
- 	char	   *result = NULL;
- 
- 	tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
- 	if (HeapTupleIsValid(tup))
- 	{
- 		result = pstrdup(NameStr(((Form_pg_am) GETSTRUCT(tup))->amname));
- 		ReleaseSysCache(tup);
- 	}
- 	return result;
- }
- 
  /*
   * Subroutine for ALTER OPERATOR CLASS SET SCHEMA/RENAME
   *
--- 1669,1674 ----
*************** IsThereOpClassInNamespace(const char *op
*** 1697,1703 ****
  				(errcode(ERRCODE_DUPLICATE_OBJECT),
  				 errmsg("operator class \"%s\" for access method \"%s\" already exists in schema \"%s\"",
  						opcname,
! 						get_am_name(opcmethod),
  						get_namespace_name(opcnamespace))));
  }
  
--- 1688,1694 ----
  				(errcode(ERRCODE_DUPLICATE_OBJECT),
  				 errmsg("operator class \"%s\" for access method \"%s\" already exists in schema \"%s\"",
  						opcname,
! 						get_am_name(opcmethod, 'i'),
  						get_namespace_name(opcnamespace))));
  }
  
*************** IsThereOpFamilyInNamespace(const char *o
*** 1720,1744 ****
  				(errcode(ERRCODE_DUPLICATE_OBJECT),
  				 errmsg("operator family \"%s\" for access method \"%s\" already exists in schema \"%s\"",
  						opfname,
! 						get_am_name(opfmethod),
  						get_namespace_name(opfnamespace))));
  }
- 
- /*
-  * get_am_oid - given an access method name, look up the OID
-  *
-  * If missing_ok is false, throw an error if access method not found.  If
-  * true, just return InvalidOid.
-  */
- Oid
- get_am_oid(const char *amname, bool missing_ok)
- {
- 	Oid			oid;
- 
- 	oid = GetSysCacheOid1(AMNAME, CStringGetDatum(amname));
- 	if (!OidIsValid(oid) && !missing_ok)
- 		ereport(ERROR,
- 				(errcode(ERRCODE_UNDEFINED_OBJECT),
- 				 errmsg("access method \"%s\" does not exist", amname)));
- 	return oid;
- }
--- 1711,1716 ----
  				(errcode(ERRCODE_DUPLICATE_OBJECT),
  				 errmsg("operator family \"%s\" for access method \"%s\" already exists in schema \"%s\"",
  						opfname,
! 						get_am_name(opfmethod, 'i'),
  						get_namespace_name(opfnamespace))));
  }
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
new file mode 100644
index a9e9cc3..85bd5ac
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copyCreateTransformStmt(const CreateTra
*** 3828,3833 ****
--- 3828,3845 ----
  	return newnode;
  }
  
+ static CreateAmStmt *
+ _copyCreateAmStmt(const CreateAmStmt *from)
+ {
+ 	CreateAmStmt *newnode = makeNode(CreateAmStmt);
+ 
+ 	COPY_STRING_FIELD(amname);
+ 	COPY_NODE_FIELD(handler_name);
+ 	COPY_SCALAR_FIELD(amtype);
+ 
+ 	return newnode;
+ }
+ 
  static CreateTrigStmt *
  _copyCreateTrigStmt(const CreateTrigStmt *from)
  {
*************** copyObject(const void *from)
*** 4819,4824 ****
--- 4831,4839 ----
  		case T_CreateTransformStmt:
  			retval = _copyCreateTransformStmt(from);
  			break;
+ 		case T_CreateAmStmt:
+ 			retval = _copyCreateAmStmt(from);
+ 			break;
  		case T_CreateTrigStmt:
  			retval = _copyCreateTrigStmt(from);
  			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
new file mode 100644
index b9c3959..f19fde1
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
*************** _equalCreateTransformStmt(const CreateTr
*** 1855,1860 ****
--- 1855,1870 ----
  }
  
  static bool
+ _equalCreateAmStmt(const CreateAmStmt *a, const CreateAmStmt *b)
+ {
+ 	COMPARE_STRING_FIELD(amname);
+ 	COMPARE_NODE_FIELD(handler_name);
+ 	COMPARE_SCALAR_FIELD(amtype);
+ 
+ 	return true;
+ }
+ 
+ static bool
  _equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b)
  {
  	COMPARE_STRING_FIELD(trigname);
*************** equal(const void *a, const void *b)
*** 3146,3151 ****
--- 3156,3164 ----
  		case T_CreateTransformStmt:
  			retval = _equalCreateTransformStmt(a, b);
  			break;
+ 		case T_CreateAmStmt:
+ 			retval = _equalCreateAmStmt(a, b);
+ 			break;
  		case T_CreateTrigStmt:
  			retval = _equalCreateTrigStmt(a, b);
  			break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
new file mode 100644
index b307b48..fe95580
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
*************** static Node *makeRecursiveViewSelect(cha
*** 263,269 ****
  		DeallocateStmt PrepareStmt ExecuteStmt
  		DropOwnedStmt ReassignOwnedStmt
  		AlterTSConfigurationStmt AlterTSDictionaryStmt
! 		CreateMatViewStmt RefreshMatViewStmt
  
  %type <node>	select_no_parens select_with_parens select_clause
  				simple_select values_clause
--- 263,269 ----
  		DeallocateStmt PrepareStmt ExecuteStmt
  		DropOwnedStmt ReassignOwnedStmt
  		AlterTSConfigurationStmt AlterTSDictionaryStmt
! 		CreateMatViewStmt RefreshMatViewStmt CreateAmStmt DropAmStmt
  
  %type <node>	select_no_parens select_with_parens select_clause
  				simple_select values_clause
*************** static Node *makeRecursiveViewSelect(cha
*** 604,610 ****
  	LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
  	LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
  
! 	MAPPING MATCH MATERIALIZED MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
  
  	NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NONE
  	NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
--- 604,610 ----
  	LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
  	LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
  
! 	MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE
  
  	NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NONE
  	NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
*************** stmt :
*** 789,794 ****
--- 789,795 ----
  			| CommentStmt
  			| ConstraintsSetStmt
  			| CopyStmt
+ 			| CreateAmStmt
  			| CreateAsStmt
  			| CreateAssertStmt
  			| CreateCastStmt
*************** stmt :
*** 823,828 ****
--- 824,830 ----
  			| DeleteStmt
  			| DiscardStmt
  			| DoStmt
+ 			| DropAmStmt
  			| DropAssertStmt
  			| DropCastStmt
  			| DropFdwStmt
*************** row_security_cmd:
*** 4708,4713 ****
--- 4710,4764 ----
  
  /*****************************************************************************
   *
+  *		QUERY:
+  *             CREATE ACCESS METHOD name HANDLER handler_name
+  *
+  *****************************************************************************/
+ 
+ CreateAmStmt: CREATE ACCESS METHOD name TYPE_P INDEX HANDLER handler_name
+ 				{
+ 					CreateAmStmt *n = makeNode(CreateAmStmt);
+ 					n->amname = $4;
+ 					n->handler_name = $8;
+ 					n->amtype = 'i';
+ 					$$ = (Node *) n;
+ 				}
+ 		;
+ 
+ /*****************************************************************************
+  *
+  *		QUERY :
+  *				DROP ACCESS METHOD name
+  *
+  ****************************************************************************/
+ 
+ DropAmStmt: DROP ACCESS METHOD name opt_drop_behavior
+ 				{
+ 					DropStmt *n = makeNode(DropStmt);
+ 					n->removeType = OBJECT_ACCESS_METHOD;
+ 					n->objects = list_make1(list_make1(makeString($4)));
+ 					n->arguments = NIL;
+ 					n->missing_ok = false;
+ 					n->behavior = $5;
+ 					n->concurrent = false;
+ 					$$ = (Node *) n;
+ 				}
+ 				|  DROP ACCESS METHOD IF_P EXISTS name opt_drop_behavior
+ 				{
+ 					DropStmt *n = makeNode(DropStmt);
+ 					n->removeType = OBJECT_ACCESS_METHOD;
+ 					n->objects = list_make1(list_make1(makeString($6)));
+ 					n->arguments = NIL;
+ 					n->missing_ok = true;
+ 					n->behavior = $7;
+ 					n->concurrent = false;
+ 					$$ = (Node *) n;
+ 				}
+ 		;
+ 
+ 
+ /*****************************************************************************
+  *
   *		QUERIES :
   *				CREATE TRIGGER ...
   *				DROP TRIGGER ...
*************** unreserved_keyword:
*** 13778,13783 ****
--- 13829,13835 ----
  			| MATCH
  			| MATERIALIZED
  			| MAXVALUE
+ 			| METHOD
  			| MINUTE_P
  			| MINVALUE
  			| MODE
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
new file mode 100644
index dc431c7..d92864c
*** a/src/backend/parser/parse_utilcmd.c
--- b/src/backend/parser/parse_utilcmd.c
*************** transformIndexConstraint(Constraint *con
*** 1709,1715 ****
  		 * else dump and reload will produce a different index (breaking
  		 * pg_upgrade in particular).
  		 */
! 		if (index_rel->rd_rel->relam != get_am_oid(DEFAULT_INDEX_TYPE, false))
  			ereport(ERROR,
  					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
  					 errmsg("index \"%s\" is not a btree", index_name),
--- 1709,1715 ----
  		 * else dump and reload will produce a different index (breaking
  		 * pg_upgrade in particular).
  		 */
! 		if (index_rel->rd_rel->relam != get_am_oid(DEFAULT_INDEX_TYPE, 'i', false))
  			ereport(ERROR,
  					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
  					 errmsg("index \"%s\" is not a btree", index_name),
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
new file mode 100644
index 045f7f0..4d0aac9
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
*************** ProcessUtilitySlow(Node *parsetree,
*** 1520,1525 ****
--- 1520,1529 ----
  				address = ExecSecLabelStmt((SecLabelStmt *) parsetree);
  				break;
  
+ 			case T_CreateAmStmt:
+ 				address = CreateAccessMethod((CreateAmStmt *) parsetree);
+ 				break;
+ 
  			default:
  				elog(ERROR, "unrecognized node type: %d",
  					 (int) nodeTag(parsetree));
*************** CreateCommandTag(Node *parsetree)
*** 2160,2165 ****
--- 2164,2172 ----
  				case OBJECT_TRANSFORM:
  					tag = "DROP TRANSFORM";
  					break;
+ 				case OBJECT_ACCESS_METHOD:
+ 					tag = "DROP ACCESS METHOD";
+ 					break;
  				default:
  					tag = "???";
  			}
*************** CreateCommandTag(Node *parsetree)
*** 2256,2261 ****
--- 2263,2271 ----
  				case OBJECT_COLLATION:
  					tag = "CREATE COLLATION";
  					break;
+ 				case OBJECT_ACCESS_METHOD:
+ 					tag = "CREATE ACCESS METHOD";
+ 					break;
  				default:
  					tag = "???";
  			}
*************** CreateCommandTag(Node *parsetree)
*** 2519,2524 ****
--- 2529,2538 ----
  			tag = "ALTER POLICY";
  			break;
  
+ 		case T_CreateAmStmt:
+ 			tag = "CREATE ACCESS METHOD";
+ 			break;
+ 
  		case T_PrepareStmt:
  			tag = "PREPARE";
  			break;
*************** GetCommandLogLevel(Node *parsetree)
*** 3076,3081 ****
--- 3090,3099 ----
  			lev = LOGSTMT_DDL;
  			break;
  
+ 		case T_CreateAmStmt:
+ 			lev = LOGSTMT_DDL;
+ 			break;
+ 
  			/* already-planned queries */
  		case T_PlannedStmt:
  			{
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
new file mode 100644
index 46c95b0..516af92
*** a/src/backend/utils/adt/selfuncs.c
--- b/src/backend/utils/adt/selfuncs.c
*************** string_to_bytea_const(const char *str, s
*** 6012,6032 ****
   *-------------------------------------------------------------------------
   */
  
! /*
!  * deconstruct_indexquals is a simple function to examine the indexquals
!  * attached to a proposed IndexPath.  It returns a list of IndexQualInfo
!  * structs, one per qual expression.
!  */
! typedef struct
! {
! 	RestrictInfo *rinfo;		/* the indexqual itself */
! 	int			indexcol;		/* zero-based index column number */
! 	bool		varonleft;		/* true if index column is on left of qual */
! 	Oid			clause_op;		/* qual's operator OID, if relevant */
! 	Node	   *other_operand;	/* non-index operand of qual's operator */
! } IndexQualInfo;
! 
! static List *
  deconstruct_indexquals(IndexPath *path)
  {
  	List	   *result = NIL;
--- 6012,6018 ----
   *-------------------------------------------------------------------------
   */
  
! List *
  deconstruct_indexquals(IndexPath *path)
  {
  	List	   *result = NIL;
*************** orderby_operands_eval_cost(PlannerInfo *
*** 6176,6210 ****
  	return qual_arg_cost;
  }
  
! /*
!  * genericcostestimate is a general-purpose estimator that can be used for
!  * most index types.  In some cases we use genericcostestimate as the base
!  * code and then incorporate additional index-type-specific knowledge in
!  * the type-specific calling function.  To avoid code duplication, we make
!  * genericcostestimate return a number of intermediate values as well as
!  * its preliminary estimates of the output cost values.  The GenericCosts
!  * struct includes all these values.
!  *
!  * Callers should initialize all fields of GenericCosts to zero.  In addition,
!  * they can set numIndexTuples to some positive value if they have a better
!  * than default way of estimating the number of leaf index tuples visited.
!  */
! typedef struct
! {
! 	/* These are the values the cost estimator must return to the planner */
! 	Cost		indexStartupCost;		/* index-related startup cost */
! 	Cost		indexTotalCost; /* total index-related scan cost */
! 	Selectivity indexSelectivity;		/* selectivity of index */
! 	double		indexCorrelation;		/* order correlation of index */
! 
! 	/* Intermediate values we obtain along the way */
! 	double		numIndexPages;	/* number of leaf pages visited */
! 	double		numIndexTuples; /* number of leaf tuples visited */
! 	double		spc_random_page_cost;	/* relevant random_page_cost value */
! 	double		num_sa_scans;	/* # indexscans from ScalarArrayOps */
! } GenericCosts;
! 
! static void
  genericcostestimate(PlannerInfo *root,
  					IndexPath *path,
  					double loop_count,
--- 6162,6168 ----
  	return qual_arg_cost;
  }
  
! void
  genericcostestimate(PlannerInfo *root,
  					IndexPath *path,
  					double loop_count,
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
new file mode 100644
index f798b15..1acd91a
*** a/src/bin/pg_dump/common.c
--- b/src/bin/pg_dump/common.c
*************** getSchemaData(Archive *fout, int *numTab
*** 98,103 ****
--- 98,104 ----
  	int			numProcLangs;
  	int			numCasts;
  	int			numTransforms;
+ 	int			numAccessMethods;
  	int			numOpclasses;
  	int			numOpfamilies;
  	int			numConversions;
*************** getSchemaData(Archive *fout, int *numTab
*** 169,174 ****
--- 170,179 ----
  	oprinfoindex = buildIndexArray(oprinfo, numOperators, sizeof(OprInfo));
  
  	if (g_verbose)
+ 		write_msg(NULL, "reading user-defined access methods\n");
+ 	getAccessMethods(fout, &numAccessMethods);
+ 
+ 	if (g_verbose)
  		write_msg(NULL, "reading user-defined operator classes\n");
  	getOpclasses(fout, &numOpclasses);
  
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
new file mode 100644
index 64c2673..8999bcc
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
*************** static void dumpFunc(Archive *fout, Func
*** 173,178 ****
--- 173,179 ----
  static void dumpCast(Archive *fout, CastInfo *cast);
  static void dumpTransform(Archive *fout, TransformInfo *transform);
  static void dumpOpr(Archive *fout, OprInfo *oprinfo);
+ static void dumpAccessMethod(Archive *fout, AccessMethodInfo *oprinfo);
  static void dumpOpclass(Archive *fout, OpclassInfo *opcinfo);
  static void dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo);
  static void dumpCollation(Archive *fout, CollInfo *convinfo);
*************** getConversions(Archive *fout, int *numCo
*** 4101,4106 ****
--- 4102,4183 ----
  }
  
  /*
+  * getAccessMethods:
+  *	  read all user-defined access methods in the system catalogs and return
+  *    them in the AccessMethodInfo* structure
+  *
+  *	numAccessMethods is set to the number of access methods read in
+  */
+ AccessMethodInfo *
+ getAccessMethods(Archive *fout, int *numAccessMethods)
+ {
+ 	DumpOptions *dopt = fout->dopt;
+ 	PGresult   *res;
+ 	int			ntups;
+ 	int			i;
+ 	PQExpBuffer query;
+ 	AccessMethodInfo *aminfo;
+ 	int			i_tableoid;
+ 	int			i_oid;
+ 	int			i_amname;
+ 	int			i_amhandler;
+ 
+ 	/* Before 9.6, there are no user-defined access methods */
+ 	if (fout->remoteVersion < 90600)
+ 	{
+ 		*numAccessMethods = 0;
+ 		return NULL;
+ 	}
+ 
+ 	query = createPQExpBuffer();
+ 
+ 	/* Make sure we are in proper schema */
+ 	selectSourceSchema(fout, "pg_catalog");
+ 
+ 	/*
+ 	 * Select only user-defined access methods assuming all built-in access
+ 	 * methods have oid < 10000.
+ 	 */
+ 	appendPQExpBuffer(query, "SELECT tableoid, oid, amname, "
+ 					  "amhandler::pg_catalog.regproc AS amhandler "
+ 					  "FROM pg_am "
+ 					  "WHERE oid >= 10000");
+ 
+ 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+ 
+ 	ntups = PQntuples(res);
+ 	*numAccessMethods = ntups;
+ 
+ 	aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
+ 
+ 	i_tableoid = PQfnumber(res, "tableoid");
+ 	i_oid = PQfnumber(res, "oid");
+ 	i_amname = PQfnumber(res, "amname");
+ 	i_amhandler = PQfnumber(res, "amhandler");
+ 
+ 	for (i = 0; i < ntups; i++)
+ 	{
+ 		aminfo[i].dobj.objType = DO_ACCESS_METHOD;
+ 		aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ 		aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ 		AssignDumpId(&aminfo[i].dobj);
+ 		aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
+ 		aminfo[i].dobj.namespace = NULL;
+ 		aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
+ 
+ 		/* Decide whether we want to dump it */
+ 		selectDumpableObject(&(aminfo[i].dobj), dopt);
+ 	}
+ 
+ 	PQclear(res);
+ 
+ 	destroyPQExpBuffer(query);
+ 
+ 	return aminfo;
+ }
+ 
+ 
+ /*
   * getOpclasses:
   *	  read all opclasses in the system catalogs and return them in the
   * OpclassInfo* structure
*************** dumpDumpableObject(Archive *fout, Dumpab
*** 8408,8413 ****
--- 8485,8493 ----
  		case DO_OPERATOR:
  			dumpOpr(fout, (OprInfo *) dobj);
  			break;
+ 		case DO_ACCESS_METHOD:
+ 			dumpAccessMethod(fout, (AccessMethodInfo *) dobj);
+ 			break;
  		case DO_OPCLASS:
  			dumpOpclass(fout, (OpclassInfo *) dobj);
  			break;
*************** convertTSFunction(Archive *fout, Oid fun
*** 11446,11451 ****
--- 11526,11584 ----
  	return result;
  }
  
+ /*
+  * dumpAccessMethod
+  *	  write out a single access method definition
+  */
+ static void
+ dumpAccessMethod(Archive *fout, AccessMethodInfo *aminfo)
+ {
+ 	DumpOptions *dopt = fout->dopt;
+ 	PQExpBuffer q;
+ 	PQExpBuffer delq;
+ 	PQExpBuffer labelq;
+ 	char	   *qamname;
+ 
+ 	/* Skip if not to be dumped */
+ 	if (!aminfo->dobj.dump || dopt->dataOnly)
+ 		return;
+ 
+ 	q = createPQExpBuffer();
+ 	delq = createPQExpBuffer();
+ 	labelq = createPQExpBuffer();
+ 
+ 	qamname = pg_strdup(fmtId(aminfo->dobj.name));
+ 
+ 	appendPQExpBuffer(q, "CREATE ACCESS METHOD %s HANDLER %s;\n",
+ 					  qamname, aminfo->amhandler);
+ 
+ 	appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
+ 					  qamname);
+ 
+ 	appendPQExpBuffer(labelq, "ACCESS METHOD %s",
+ 					  qamname);
+ 
+ 	ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
+ 				 aminfo->dobj.name,
+ 				 NULL,
+ 				 NULL,
+ 				 "",
+ 				 false, "ACCESS METHOD", SECTION_PRE_DATA,
+ 				 q->data, delq->data, NULL,
+ 				 NULL, 0,
+ 				 NULL, NULL);
+ 
+ 	/* Dump Access Method Comments */
+ 	dumpComment(fout, labelq->data,
+ 				NULL, "",
+ 				aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
+ 
+ 	free(qamname);
+ 
+ 	destroyPQExpBuffer(q);
+ 	destroyPQExpBuffer(delq);
+ 	destroyPQExpBuffer(labelq);
+ }
  
  /*
   * dumpOpclass
*************** addBoundaryDependencies(DumpableObject *
*** 16227,16232 ****
--- 16360,16366 ----
  			case DO_FUNC:
  			case DO_AGG:
  			case DO_OPERATOR:
+ 			case DO_ACCESS_METHOD:
  			case DO_OPCLASS:
  			case DO_OPFAMILY:
  			case DO_COLLATION:
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
new file mode 100644
index 9a1d8f8..ad37047
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
*************** typedef enum
*** 48,53 ****
--- 48,54 ----
  	DO_FUNC,
  	DO_AGG,
  	DO_OPERATOR,
+ 	DO_ACCESS_METHOD,
  	DO_OPCLASS,
  	DO_OPFAMILY,
  	DO_COLLATION,
*************** typedef enum
*** 77,83 ****
  	DO_POST_DATA_BOUNDARY,
  	DO_EVENT_TRIGGER,
  	DO_REFRESH_MATVIEW,
! 	DO_POLICY
  } DumpableObjectType;
  
  typedef struct _dumpableObject
--- 78,84 ----
  	DO_POST_DATA_BOUNDARY,
  	DO_EVENT_TRIGGER,
  	DO_REFRESH_MATVIEW,
! 	DO_POLICY,
  } DumpableObjectType;
  
  typedef struct _dumpableObject
*************** typedef struct _oprInfo
*** 167,172 ****
--- 168,179 ----
  	Oid			oprcode;
  } OprInfo;
  
+ typedef struct _accessMethodInfo
+ {
+ 	DumpableObject dobj;
+ 	char	   *amhandler;
+ } AccessMethodInfo;
+ 
  typedef struct _opclassInfo
  {
  	DumpableObject dobj;
*************** extern TypeInfo *getTypes(Archive *fout,
*** 548,553 ****
--- 555,561 ----
  extern FuncInfo *getFuncs(Archive *fout, int *numFuncs);
  extern AggInfo *getAggregates(Archive *fout, int *numAggregates);
  extern OprInfo *getOperators(Archive *fout, int *numOperators);
+ extern AccessMethodInfo *getAccessMethods(Archive *fout, int *numAccessMethods);
  extern OpclassInfo *getOpclasses(Archive *fout, int *numOpclasses);
  extern OpfamilyInfo *getOpfamilies(Archive *fout, int *numOpfamilies);
  extern CollInfo *getCollations(Archive *fout, int *numCollations);
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
new file mode 100644
index 78ff59c..3bd5bd8
*** a/src/bin/pg_dump/pg_dump_sort.c
--- b/src/bin/pg_dump/pg_dump_sort.c
*************** static const int oldObjectTypePriority[]
*** 45,50 ****
--- 45,51 ----
  	2,							/* DO_FUNC */
  	3,							/* DO_AGG */
  	3,							/* DO_OPERATOR */
+ 	3,							/* DO_ACCESS_METHOD */
  	4,							/* DO_OPCLASS */
  	4,							/* DO_OPFAMILY */
  	4,							/* DO_COLLATION */
*************** static const int newObjectTypePriority[]
*** 95,100 ****
--- 96,102 ----
  	6,							/* DO_FUNC */
  	7,							/* DO_AGG */
  	8,							/* DO_OPERATOR */
+ 	8,							/* DO_ACCESS_METHOD */
  	9,							/* DO_OPCLASS */
  	9,							/* DO_OPFAMILY */
  	3,							/* DO_COLLATION */
*************** describeDumpableObject(DumpableObject *o
*** 1329,1334 ****
--- 1331,1340 ----
  					 "OPERATOR %s  (ID %d OID %u)",
  					 obj->name, obj->dumpId, obj->catId.oid);
  			return;
+ 		case DO_ACCESS_METHOD:
+ 			snprintf(buf, bufsize,
+ 					 "");
+ 			return;
  		case DO_OPCLASS:
  			snprintf(buf, bufsize,
  					 "OPERATOR CLASS %s  (ID %d OID %u)",
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
new file mode 100644
index 049bf9f..ac16740
*** a/src/include/catalog/dependency.h
--- b/src/include/catalog/dependency.h
*************** typedef enum ObjectClass
*** 153,162 ****
  	OCLASS_EXTENSION,			/* pg_extension */
  	OCLASS_EVENT_TRIGGER,		/* pg_event_trigger */
  	OCLASS_POLICY,				/* pg_policy */
! 	OCLASS_TRANSFORM			/* pg_transform */
  } ObjectClass;
  
! #define LAST_OCLASS		OCLASS_TRANSFORM
  
  
  /* in dependency.c */
--- 153,163 ----
  	OCLASS_EXTENSION,			/* pg_extension */
  	OCLASS_EVENT_TRIGGER,		/* pg_event_trigger */
  	OCLASS_POLICY,				/* pg_policy */
! 	OCLASS_TRANSFORM,			/* pg_transform */
! 	OCLASS_AM,					/* pg_am */
  } ObjectClass;
  
! #define LAST_OCLASS		OCLASS_AM
  
  
  /* in dependency.c */
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
new file mode 100644
index f801c3e..605e154
*** a/src/include/catalog/pg_am.h
--- b/src/include/catalog/pg_am.h
*************** CATALOG(pg_am,2601)
*** 35,40 ****
--- 35,47 ----
  {
  	NameData	amname;			/* access method name */
  	regproc		amhandler;		/* handler function */
+ 
+ 	/*----------
+ 	 * Type of access method. Possible values are
+ 	 *		'i': Index access method
+ 	 *----------
+ 	 */
+ 	char		amtype;
  } FormData_pg_am;
  
  /* ----------------
*************** typedef FormData_pg_am *Form_pg_am;
*** 48,78 ****
   *		compiler constants for pg_am
   * ----------------
   */
! #define Natts_pg_am						2
  #define Anum_pg_am_amname				1
  #define Anum_pg_am_amhandler			2
  
  /* ----------------
   *		initial contents of pg_am
   * ----------------
   */
  
! DATA(insert OID = 403 (  btree		bthandler ));
  DESCR("b-tree index access method");
  #define BTREE_AM_OID 403
! DATA(insert OID = 405 (  hash		hashhandler ));
  DESCR("hash index access method");
  #define HASH_AM_OID 405
! DATA(insert OID = 783 (  gist		gisthandler ));
  DESCR("GiST index access method");
  #define GIST_AM_OID 783
! DATA(insert OID = 2742 (  gin		ginhandler ));
  DESCR("GIN index access method");
  #define GIN_AM_OID 2742
! DATA(insert OID = 4000 (  spgist	spghandler ));
  DESCR("SP-GiST index access method");
  #define SPGIST_AM_OID 4000
! DATA(insert OID = 3580 (  brin		brinhandler ));
  DESCR("block range index (BRIN) access method");
  #define BRIN_AM_OID 3580
  
--- 55,86 ----
   *		compiler constants for pg_am
   * ----------------
   */
! #define Natts_pg_am						3
  #define Anum_pg_am_amname				1
  #define Anum_pg_am_amhandler			2
+ #define Anum_pg_am_amtype				3
  
  /* ----------------
   *		initial contents of pg_am
   * ----------------
   */
  
! DATA(insert OID = 403 (  btree		bthandler	i ));
  DESCR("b-tree index access method");
  #define BTREE_AM_OID 403
! DATA(insert OID = 405 (  hash		hashhandler	i ));
  DESCR("hash index access method");
  #define HASH_AM_OID 405
! DATA(insert OID = 783 (  gist		gisthandler	i ));
  DESCR("GiST index access method");
  #define GIST_AM_OID 783
! DATA(insert OID = 2742 (  gin		ginhandler	i ));
  DESCR("GIN index access method");
  #define GIN_AM_OID 2742
! DATA(insert OID = 4000 (  spgist	spghandler	i ));
  DESCR("SP-GiST index access method");
  #define SPGIST_AM_OID 4000
! DATA(insert OID = 3580 (  brin		brinhandler	i ));
  DESCR("block range index (BRIN) access method");
  #define BRIN_AM_OID 3580
  
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
new file mode 100644
index 54f67e9..6df15ed
*** a/src/include/commands/defrem.h
--- b/src/include/commands/defrem.h
*************** extern void IsThereOpClassInNamespace(co
*** 91,98 ****
  						  Oid opcnamespace);
  extern void IsThereOpFamilyInNamespace(const char *opfname, Oid opfmethod,
  						   Oid opfnamespace);
- extern Oid	get_am_oid(const char *amname, bool missing_ok);
- extern char *get_am_name(Oid amOid);
  extern Oid	get_opclass_oid(Oid amID, List *opclassname, bool missing_ok);
  extern Oid	get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok);
  
--- 91,96 ----
*************** extern Datum transformGenericOptions(Oid
*** 137,142 ****
--- 135,146 ----
  						List *options,
  						Oid fdwvalidator);
  
+ /* commands/amcmds.c */
+ extern ObjectAddress CreateAccessMethod(CreateAmStmt *stmt);
+ extern void RemoveAccessMethodById(Oid amOid);
+ extern Oid	get_am_oid(const char *amname, char amtype, bool missing_ok);
+ extern char *get_am_name(Oid amOid, char amtype);
+ 
  /* support routines in commands/define.c */
  
  extern char *defGetString(DefElem *def);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
new file mode 100644
index c407fa2..7d46763
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
*************** typedef enum NodeTag
*** 386,391 ****
--- 386,392 ----
  	T_CreatePolicyStmt,
  	T_AlterPolicyStmt,
  	T_CreateTransformStmt,
+ 	T_CreateAmStmt,
  
  	/*
  	 * TAGS FOR PARSE TREE NODES (parsenodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
new file mode 100644
index 2fd0629..8b958b4
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
*************** typedef struct SetOperationStmt
*** 1379,1384 ****
--- 1379,1385 ----
  
  typedef enum ObjectType
  {
+ 	OBJECT_ACCESS_METHOD,
  	OBJECT_AGGREGATE,
  	OBJECT_AMOP,
  	OBJECT_AMPROC,
*************** typedef struct AlterPolicyStmt
*** 2070,2075 ****
--- 2071,2088 ----
  	Node	   *with_check;		/* the policy's WITH CHECK condition. */
  } AlterPolicyStmt;
  
+ /*----------------------
+  *		Create ACCESS METHOD Statement
+  *----------------------
+  */
+ typedef struct CreateAmStmt
+ {
+ 	NodeTag		type;
+ 	char	   *amname;			/* access method name */
+ 	List	   *handler_name;	/* handler function name */
+ 	char		amtype;			/* type of access method */
+ } CreateAmStmt;
+ 
  /* ----------------------
   *		Create TRIGGER Statement
   * ----------------------
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
new file mode 100644
index 6e1e820..7de3404
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
*************** PG_KEYWORD("mapping", MAPPING, UNRESERVE
*** 239,244 ****
--- 239,245 ----
  PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD)
  PG_KEYWORD("materialized", MATERIALIZED, UNRESERVED_KEYWORD)
  PG_KEYWORD("maxvalue", MAXVALUE, UNRESERVED_KEYWORD)
+ PG_KEYWORD("method", METHOD, UNRESERVED_KEYWORD)
  PG_KEYWORD("minute", MINUTE_P, UNRESERVED_KEYWORD)
  PG_KEYWORD("minvalue", MINVALUE, UNRESERVED_KEYWORD)
  PG_KEYWORD("mode", MODE, UNRESERVED_KEYWORD)
diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h
new file mode 100644
index 06fbca7..7fb7466
*** a/src/include/utils/selfuncs.h
--- b/src/include/utils/selfuncs.h
*************** typedef enum
*** 95,100 ****
--- 95,142 ----
  	Pattern_Prefix_None, Pattern_Prefix_Partial, Pattern_Prefix_Exact
  } Pattern_Prefix_Status;
  
+ /*
+  * deconstruct_indexquals is a simple function to examine the indexquals
+  * attached to a proposed IndexPath.  It returns a list of IndexQualInfo
+  * structs, one per qual expression.
+  */
+ typedef struct
+ {
+ 	RestrictInfo *rinfo;		/* the indexqual itself */
+ 	int			indexcol;		/* zero-based index column number */
+ 	bool		varonleft;		/* true if index column is on left of qual */
+ 	Oid			clause_op;		/* qual's operator OID, if relevant */
+ 	Node	   *other_operand;	/* non-index operand of qual's operator */
+ } IndexQualInfo;
+ 
+ /*
+  * genericcostestimate is a general-purpose estimator that can be used for
+  * most index types.  In some cases we use genericcostestimate as the base
+  * code and then incorporate additional index-type-specific knowledge in
+  * the type-specific calling function.  To avoid code duplication, we make
+  * genericcostestimate return a number of intermediate values as well as
+  * its preliminary estimates of the output cost values.  The GenericCosts
+  * struct includes all these values.
+  *
+  * Callers should initialize all fields of GenericCosts to zero.  In addition,
+  * they can set numIndexTuples to some positive value if they have a better
+  * than default way of estimating the number of leaf index tuples visited.
+  */
+ typedef struct
+ {
+ 	/* These are the values the cost estimator must return to the planner */
+ 	Cost		indexStartupCost;		/* index-related startup cost */
+ 	Cost		indexTotalCost; /* total index-related scan cost */
+ 	Selectivity indexSelectivity;		/* selectivity of index */
+ 	double		indexCorrelation;		/* order correlation of index */
+ 
+ 	/* Intermediate values we obtain along the way */
+ 	double		numIndexPages;	/* number of leaf pages visited */
+ 	double		numIndexTuples; /* number of leaf tuples visited */
+ 	double		spc_random_page_cost;	/* relevant random_page_cost value */
+ 	double		num_sa_scans;	/* # indexscans from ScalarArrayOps */
+ } GenericCosts;
+ 
  /* Hooks for plugins to get control when we ask for stats */
  typedef bool (*get_relation_stats_hook_type) (PlannerInfo *root,
  														  RangeTblEntry *rte,
*************** extern double estimate_num_groups(Planne
*** 191,196 ****
--- 233,244 ----
  extern Selectivity estimate_hash_bucketsize(PlannerInfo *root, Node *hashkey,
  						 double nbuckets);
  
+ extern List *deconstruct_indexquals(IndexPath *path);
+ extern void genericcostestimate(PlannerInfo *root, IndexPath *path,
+ 								double loop_count,
+ 								List *qinfos,
+ 								GenericCosts *costs);
+ 
  /* Functions in array_selfuncs.c */
  
  extern Selectivity scalararraysel_containment(PlannerInfo *root,
diff --git a/src/test/regress/expected/create_am.out b/src/test/regress/expected/create_am.out
new file mode 100644
index ...47d6024
*** a/src/test/regress/expected/create_am.out
--- b/src/test/regress/expected/create_am.out
***************
*** 0 ****
--- 1,108 ----
+ --
+ -- Create access method tests
+ --
+ -- Make gist2 over gisthandler. In fact, it would be a synonym to gist.
+ CREATE ACCESS METHOD gist2 TYPE INDEX HANDLER gisthandler;
+ -- Drop old index on fast_emp4000
+ DROP INDEX grect2ind;
+ -- Try to create gist2 index on fast_emp4000: fail because opclass doesn't exist
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ ERROR:  data type box has no default operator class for access method "gist2"
+ HINT:  You must specify an operator class for the index or define a default operator class for the data type.
+ -- Make operator class for boxes using gist2
+ CREATE OPERATOR CLASS box_ops DEFAULT
+ 	FOR TYPE box USING gist2 AS
+ 	OPERATOR 1	<<,
+ 	OPERATOR 2	&<,
+ 	OPERATOR 3	&&,
+ 	OPERATOR 4	&>,
+ 	OPERATOR 5	>>,
+ 	OPERATOR 6	~=,
+ 	OPERATOR 7	@>,
+ 	OPERATOR 8	<@,
+ 	OPERATOR 9	&<|,
+ 	OPERATOR 10	<<|,
+ 	OPERATOR 11	|>>,
+ 	OPERATOR 12	|&>,
+ 	OPERATOR 13	~,
+ 	OPERATOR 14	@,
+ 	FUNCTION 1	gist_box_consistent(internal, box, smallint, oid, internal),
+ 	FUNCTION 2	gist_box_union(internal, internal),
+ 	FUNCTION 3	gist_box_compress(internal),
+ 	FUNCTION 4	gist_box_decompress(internal),
+ 	FUNCTION 5	gist_box_penalty(internal, internal, internal),
+ 	FUNCTION 6	gist_box_picksplit(internal, internal),
+ 	FUNCTION 7	gist_box_same(box, box, internal),
+ 	FUNCTION 9	gist_box_fetch(internal);
+ -- Create gist2 index on fast_emp4000
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ -- Now check the results from plain indexscan
+ SET enable_seqscan = OFF;
+ SET enable_indexscan = ON;
+ SET enable_bitmapscan = OFF;
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+                            QUERY PLAN                           
+ ----------------------------------------------------------------
+  Sort
+    Sort Key: ((home_base[0])[0])
+    ->  Index Only Scan using grect2ind on fast_emp4000
+          Index Cond: (home_base @ '(2000,1000),(200,200)'::box)
+ (4 rows)
+ 
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+        home_base       
+ -----------------------
+  (337,455),(240,359)
+  (1444,403),(1346,344)
+ (2 rows)
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+                          QUERY PLAN                          
+ -------------------------------------------------------------
+  Aggregate
+    ->  Index Only Scan using grect2ind on fast_emp4000
+          Index Cond: (home_base && '(1000,1000),(0,0)'::box)
+ (3 rows)
+ 
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+  count 
+ -------
+      2
+ (1 row)
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+                       QUERY PLAN                       
+ -------------------------------------------------------
+  Aggregate
+    ->  Index Only Scan using grect2ind on fast_emp4000
+          Index Cond: (home_base IS NULL)
+ (3 rows)
+ 
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+  count 
+ -------
+    278
+ (1 row)
+ 
+ -- Try to drop access method: fail because of depending objects
+ DROP ACCESS METHOD gist2;
+ ERROR:  cannot drop access method gist2 because other objects depend on it
+ DETAIL:  operator class box_ops for access method gist2 depends on access method gist2
+ index grect2ind depends on operator class box_ops for access method gist2
+ HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+ -- Drop access method cascade
+ DROP ACCESS METHOD gist2 CASCADE;
+ NOTICE:  drop cascades to 2 other objects
+ DETAIL:  drop cascades to operator class box_ops for access method gist2
+ drop cascades to index grect2ind
+ -- Reset optimizer options
+ RESET enable_seqscan;
+ RESET enable_indexscan;
+ RESET enable_bitmapscan;
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
new file mode 100644
index eb0bc88..2c5be4b
*** a/src/test/regress/expected/sanity_check.out
--- b/src/test/regress/expected/sanity_check.out
*************** e_star|f
*** 44,50 ****
  emp|f
  equipment_r|f
  f_star|f
! fast_emp4000|t
  float4_tbl|f
  float8_tbl|f
  func_index_heap|t
--- 44,50 ----
  emp|f
  equipment_r|f
  f_star|f
! fast_emp4000|f
  float4_tbl|f
  float8_tbl|f
  func_index_heap|t
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
new file mode 100644
index bec0316..8be4b83
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
*************** test: create_index create_view
*** 60,66 ****
  # ----------
  # Another group of parallel tests
  # ----------
! test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes
  
  # ----------
  # sanity_check does a vacuum, affecting the sort order of SELECT *
--- 60,66 ----
  # ----------
  # Another group of parallel tests
  # ----------
! test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes create_am
  
  # ----------
  # sanity_check does a vacuum, affecting the sort order of SELECT *
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
new file mode 100644
index 7e9b319..1de3da8
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
*************** test: drop_if_exists
*** 75,80 ****
--- 75,81 ----
  test: updatable_views
  test: rolenames
  test: roleattributes
+ test: create_am
  test: sanity_check
  test: errors
  test: select
diff --git a/src/test/regress/sql/create_am.sql b/src/test/regress/sql/create_am.sql
new file mode 100644
index ...e2051c5
*** a/src/test/regress/sql/create_am.sql
--- b/src/test/regress/sql/create_am.sql
***************
*** 0 ****
--- 1,73 ----
+ --
+ -- Create access method tests
+ --
+ 
+ -- Make gist2 over gisthandler. In fact, it would be a synonym to gist.
+ CREATE ACCESS METHOD gist2 TYPE INDEX HANDLER gisthandler;
+ 
+ -- Drop old index on fast_emp4000
+ DROP INDEX grect2ind;
+ 
+ -- Try to create gist2 index on fast_emp4000: fail because opclass doesn't exist
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ 
+ -- Make operator class for boxes using gist2
+ CREATE OPERATOR CLASS box_ops DEFAULT
+ 	FOR TYPE box USING gist2 AS
+ 	OPERATOR 1	<<,
+ 	OPERATOR 2	&<,
+ 	OPERATOR 3	&&,
+ 	OPERATOR 4	&>,
+ 	OPERATOR 5	>>,
+ 	OPERATOR 6	~=,
+ 	OPERATOR 7	@>,
+ 	OPERATOR 8	<@,
+ 	OPERATOR 9	&<|,
+ 	OPERATOR 10	<<|,
+ 	OPERATOR 11	|>>,
+ 	OPERATOR 12	|&>,
+ 	OPERATOR 13	~,
+ 	OPERATOR 14	@,
+ 	FUNCTION 1	gist_box_consistent(internal, box, smallint, oid, internal),
+ 	FUNCTION 2	gist_box_union(internal, internal),
+ 	FUNCTION 3	gist_box_compress(internal),
+ 	FUNCTION 4	gist_box_decompress(internal),
+ 	FUNCTION 5	gist_box_penalty(internal, internal, internal),
+ 	FUNCTION 6	gist_box_picksplit(internal, internal),
+ 	FUNCTION 7	gist_box_same(box, box, internal),
+ 	FUNCTION 9	gist_box_fetch(internal);
+ 
+ -- Create gist2 index on fast_emp4000
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ 
+ -- Now check the results from plain indexscan
+ SET enable_seqscan = OFF;
+ SET enable_indexscan = ON;
+ SET enable_bitmapscan = OFF;
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+ 
+ -- Try to drop access method: fail because of depending objects
+ DROP ACCESS METHOD gist2;
+ 
+ -- Drop access method cascade
+ DROP ACCESS METHOD gist2 CASCADE;
+ 
+ -- Reset optimizer options
+ RESET enable_seqscan;
+ RESET enable_indexscan;
+ RESET enable_bitmapscan;
generic-xlog.6.patchapplication/octet-stream; name=generic-xlog.6.patchDownload
diff --git a/src/backend/access/index/amapi.c b/src/backend/access/index/amapi.c
new file mode 100644
index bda166a..36a780a
*** a/src/backend/access/index/amapi.c
--- b/src/backend/access/index/amapi.c
*************** GetIndexAmRoutineByAmId(Oid amoid)
*** 62,67 ****
--- 62,74 ----
  			 amoid);
  	amform = (Form_pg_am) GETSTRUCT(tuple);
  
+ 	/* Check if it's index access method*/
+ 	if (amform->amtype != 'i')
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("access method \"%s\" type is not index",
+ 						NameStr(amform->amname))));
+ 
  	amhandler = amform->amhandler;
  
  	/* Complain if handler OID is invalid */
diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
new file mode 100644
index c72a1f2..c0e38fd
*** a/src/backend/access/rmgrdesc/Makefile
--- b/src/backend/access/rmgrdesc/Makefile
*************** subdir = src/backend/access/rmgrdesc
*** 8,16 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o gindesc.o gistdesc.o \
! 	   hashdesc.o heapdesc.o mxactdesc.o nbtdesc.o relmapdesc.o \
! 	   replorigindesc.o seqdesc.o smgrdesc.o spgdesc.o \
  	   standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
  
  include $(top_srcdir)/src/backend/common.mk
--- 8,16 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o genericdesc.o \
! 	   gindesc.o gistdesc.o hashdesc.o heapdesc.o mxactdesc.o nbtdesc.o \
! 	   relmapdesc.o replorigindesc.o seqdesc.o smgrdesc.o spgdesc.o \
  	   standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
  
  include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/genericdesc.c b/src/backend/access/rmgrdesc/genericdesc.c
new file mode 100644
index ...3d035c2
*** a/src/backend/access/rmgrdesc/genericdesc.c
--- b/src/backend/access/rmgrdesc/genericdesc.c
***************
*** 0 ****
--- 1,58 ----
+ /*-------------------------------------------------------------------------
+  *
+  * genericdesc.c
+  *	  rmgr descriptor routines for access/transam/generic_xlog.c
+  *
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/backend/access/rmgrdesc/genericdesc.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/generic_xlog.h"
+ #include "lib/stringinfo.h"
+ #include "storage/relfilenode.h"
+ 
+ /*
+  * Description of generic xlog record: write page regions which this record
+  * overrides.
+  */
+ void
+ generic_desc(StringInfo buf, XLogReaderState *record)
+ {
+ 	Pointer		ptr = XLogRecGetData(record),
+ 				end = ptr + XLogRecGetDataLen(record);
+ 
+ 	while (ptr < end)
+ 	{
+ 		OffsetNumber	offset,
+ 						length;
+ 
+ 		memcpy(&offset, ptr, sizeof(offset));
+ 		ptr += sizeof(offset);
+ 		memcpy(&length, ptr, sizeof(length));
+ 		ptr += sizeof(length);
+ 		ptr += length;
+ 
+ 		if (ptr < end)
+ 			appendStringInfo(buf, "offset %u, length %u; ", offset, length);
+ 		else
+ 			appendStringInfo(buf, "offset %u, length %u", offset, length);
+ 	}
+ 
+ 	return;
+ }
+ 
+ /*
+  * Identification of generic xlog record: we don't distinguish any subtypes
+  * inside generic xlog records.
+  */
+ const char *
+ generic_identify(uint8 info)
+ {
+ 	return "Generic";
+ }
diff --git a/src/backend/access/transam/Makefile b/src/backend/access/transam/Makefile
new file mode 100644
index 94455b2..16fbe47
*** a/src/backend/access/transam/Makefile
--- b/src/backend/access/transam/Makefile
*************** subdir = src/backend/access/transam
*** 12,19 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = clog.o commit_ts.o multixact.o parallel.o rmgr.o slru.o subtrans.o \
! 	timeline.o transam.o twophase.o twophase_rmgr.o varsup.o \
  	xact.o xlog.o xlogarchive.o xlogfuncs.o \
  	xloginsert.o xlogreader.o xlogutils.o
  
--- 12,19 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = clog.o commit_ts.o generic_xlog.o multixact.o parallel.o rmgr.o slru.o \
! 	subtrans.o timeline.o transam.o twophase.o twophase_rmgr.o varsup.o \
  	xact.o xlog.o xlogarchive.o xlogfuncs.o \
  	xloginsert.o xlogreader.o xlogutils.o
  
diff --git a/src/backend/access/transam/generic_xlog.c b/src/backend/access/transam/generic_xlog.c
new file mode 100644
index ...2dcba91
*** a/src/backend/access/transam/generic_xlog.c
--- b/src/backend/access/transam/generic_xlog.c
***************
*** 0 ****
--- 1,435 ----
+ /*-------------------------------------------------------------------------
+  *
+  * generic_xlog.c
+  *	 Implementation of generic xlog records.
+  *
+  *
+  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/backend/access/transam/generic_xlog.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/generic_xlog.h"
+ #include "access/xlogutils.h"
+ #include "miscadmin.h"
+ #include "utils/memutils.h"
+ 
+ #define MATCH_THRESHOLD			  4
+ 
+ /* Struct of generic xlog data for single page */
+ typedef struct
+ {
+ 	Buffer	buffer;			/* registered buffer */
+ 	char	image[BLCKSZ];	/* copy of page image for modification */
+ 	char	data[BLCKSZ + 2 * sizeof(OffsetNumber)]; /* delta between page images */
+ 	int		dataLen;		/* space consumed in data field */
+ 	bool	fullImage;		/* are we taking full image of this page? */
+ } PageData;
+ 
+ /* Enum of generic xlog (gxlog) status */
+ enum GenericXlogStatus
+ {
+ 	GXLOG_NOT_STARTED,	/* gxlog is not started */
+ 	GXLOG_LOGGED,		/* gxlog is started for logged relation */
+ 	GXLOG_UNLOGGED		/* gxlog is started for unlogged relation */
+ };
+ 
+ static enum GenericXlogStatus	genericXlogStatus = GXLOG_NOT_STARTED;
+ static PageData					pages[MAX_GENERIC_XLOG_PAGES];
+ 
+ 
+ static void writeFragment(PageData *pageData, OffsetNumber offset,
+ 						  OffsetNumber len, Pointer data);
+ static void writeDelta(PageData *pageData);
+ static void applyPageRedo(Page page, Pointer data, Size dataSize);
+ 
+ /*---
+  * Write next fragment into delta.  Each fragment represents changes maded in
+  * given region of page.  Fragment is described as following.
+  *
+  * - offset of page region (OffsetNumber)
+  * - length of page region (OffsetNumber)
+  * - data - the data to place into described region ('length' number of bytes)
+  */
+ static void
+ writeFragment(PageData *pageData, OffsetNumber offset, OffsetNumber length,
+ 			  Pointer data)
+ {
+ 	Pointer			ptr = pageData->data + pageData->dataLen;
+ 
+ 	/* Check we have enough of space */
+ 	Assert(pageData->dataLen + sizeof(offset) +
+ 		   sizeof(length) + length <= sizeof(pageData->data));
+ 
+ 	/* Write fragment data */
+ 	memcpy(ptr, &offset, sizeof(offset));
+ 	ptr += sizeof(offset);
+ 	memcpy(ptr, &length, sizeof(length));
+ 	ptr += sizeof(length);
+ 	memcpy(ptr, data, length);
+ 	ptr += length;
+ 
+ 	pageData->dataLen = ptr - pageData->data;
+ }
+ 
+ /*
+  * Make delta for given page.
+  */
+ static void
+ writeDelta(PageData *pageData)
+ {
+ 	Page			page = BufferGetPage(pageData->buffer),
+ 					image = (Page) pageData->image;
+ 	int				i,
+ 					fragmentBegin = -1,
+ 					fragmentEnd = -1;
+ 	uint16			pageLower = ((PageHeader) page)->pd_lower,
+ 					pageUpper = ((PageHeader) page)->pd_upper,
+ 					imageLower = ((PageHeader) image)->pd_lower,
+ 					imageUpper = ((PageHeader) image)->pd_upper;
+ 
+ 	for (i = 0; i < BLCKSZ; i++)
+ 	{
+ 		bool	match;
+ 
+ 		/*
+ 		 * Check if bytes in old and new page images matches.  We don't rely
+ 		 * data in unallocated area between pd_lower and pd_upper.  Thus we
+ 		 * assume unallocated area to expand with unmatched bytes.  Bytes
+ 		 * inside unallocated area are assumed to always match.
+ 		 */
+ 		if (i < pageLower)
+ 		{
+ 			if (i < imageLower)
+ 				match = (page[i] == image[i]);
+ 			else
+ 				match = false;
+ 		}
+ 		else if (i >= pageUpper)
+ 		{
+ 			if (i >= imageUpper)
+ 				match = (page[i] == image[i]);
+ 			else
+ 				match = false;
+ 		}
+ 		else
+ 		{
+ 			match = true;
+ 		}
+ 
+ 		if (match)
+ 		{
+ 			if (fragmentBegin >= 0)
+ 			{
+ 				/* Matched byte is potential of fragment. */
+ 				if (fragmentEnd < 0)
+ 					fragmentEnd = i;
+ 
+ 				/*
+ 				 * Write next fragment if sequence of matched bytes is longer
+ 				 * than MATCH_THRESHOLD.
+ 				 */
+ 				if (i - fragmentEnd >= MATCH_THRESHOLD)
+ 				{
+ 					writeFragment(pageData, fragmentBegin,
+ 								  fragmentEnd - fragmentBegin,
+ 								  page + fragmentBegin);
+ 					fragmentBegin = -1;
+ 					fragmentEnd = -1;
+ 				}
+ 			}
+ 		}
+ 		else
+ 		{
+ 			/* On unmatched byte, start new fragment if it's not done yet */
+ 			if (fragmentBegin < 0)
+ 				fragmentBegin = i;
+ 			fragmentEnd = -1;
+ 		}
+ 	}
+ 
+ 	if (fragmentBegin >= 0)
+ 		writeFragment(pageData, fragmentBegin,
+ 					  BLCKSZ - fragmentBegin,
+ 					  page + fragmentBegin);
+ 
+ #ifdef USE_ASSERT_CHECKING
+ 	/*
+ 	 * If assert checking is enabled check produced delta.  Result of delta
+ 	 * application to saved image should be the same as current page state.
+ 	 */
+ 	do
+ 	{
+ 		char	tmp[BLCKSZ];
+ 		memcpy(tmp, image, BLCKSZ);
+ 		applyPageRedo(tmp, pageData->data, pageData->dataLen);
+ 		Assert(memcmp(tmp, page, BLCKSZ) == 0);
+ 	} while (false);
+ #endif
+ }
+ 
+ /*
+  * Start new generic xlog record.
+  */
+ void
+ GenericXLogStart(Relation relation)
+ {
+ 	int i;
+ 
+ 	if (genericXlogStatus != GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogStart: generic xlog is already started")));
+ 
+ 	genericXlogStatus = RelationNeedsWAL(relation) ? GXLOG_LOGGED : GXLOG_UNLOGGED;
+ 
+ 	for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 	{
+ 		pages[i].buffer = InvalidBuffer;
+ 	}
+ }
+ 
+ /*
+  * Register new buffer for generic xlog record.
+  */
+ Page
+ GenericXLogRegister(Buffer buffer, bool isNew)
+ {
+ 	int block_id;
+ 
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogRegister: generic xlog isn't started")));
+ 
+ 	/* Place new buffer to unused slot in array */
+ 	for (block_id = 0; block_id < MAX_GENERIC_XLOG_PAGES; block_id++)
+ 	{
+ 		if (BufferIsInvalid(pages[block_id].buffer))
+ 		{
+ 			pages[block_id].buffer = buffer;
+ 			memcpy(pages[block_id].image, BufferGetPage(buffer), BLCKSZ);
+ 			pages[block_id].dataLen = 0;
+ 			pages[block_id].fullImage = isNew;
+ 			return (Page)pages[block_id].image;
+ 		}
+ 		else if (pages[block_id].buffer == buffer)
+ 		{
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 					 errmsg("GenericXLogRegister: duplicate buffer %d", buffer)));
+ 		}
+ 	}
+ 
+ 	ereport(ERROR,
+ 			(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ 			 errmsg("GenericXLogRegister: maximum number of %d buffers is exceeded",
+ 					MAX_GENERIC_XLOG_PAGES)));
+ 
+ 	/* keep compiler quiet */
+ 	return NULL;
+ }
+ 
+ /*
+  * Unregister particular buffer for generic xlog record.
+  */
+ void
+ GenericXLogUnregister(Buffer buffer)
+ {
+ 	int block_id;
+ 
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogUnregister: generic xlog isn't started")));
+ 
+ 	/* Find block in array to unregister */
+ 	for (block_id = 0; block_id < MAX_GENERIC_XLOG_PAGES; block_id++)
+ 	{
+ 		if (pages[block_id].buffer == buffer)
+ 		{
+ 			/*
+ 			 * Preserve order of pages in array because it could matter for
+ 			 * concurrency.
+ 			 */
+ 			memmove(&pages[block_id], &pages[block_id + 1],
+ 					(MAX_GENERIC_XLOG_PAGES - block_id - 1) * sizeof(PageData));
+ 			pages[MAX_GENERIC_XLOG_PAGES - 1].buffer = InvalidBuffer;
+ 			return;
+ 		}
+ 	}
+ 
+ 	ereport(ERROR,
+ 			(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 			 errmsg("GenericXLogUnregister: registered buffer not found")));
+ }
+ 
+ /*
+  * Put all changes in registered buffers to generic xlog record.
+  */
+ XLogRecPtr
+ GenericXLogFinish(void)
+ {
+ 	XLogRecPtr lsn = InvalidXLogRecPtr;
+ 	int i;
+ 
+ 	if (genericXlogStatus == GXLOG_LOGGED)
+ 	{
+ 		/* Logged relation: make xlog record in critical section. */
+ 		START_CRIT_SECTION();
+ 		XLogBeginInsert();
+ 
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			char	tmp[BLCKSZ];
+ 
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 
+ 			/* Swap current and saved page image. */
+ 			memcpy(tmp, pages[i].image, BLCKSZ);
+ 			memcpy(pages[i].image, BufferGetPage(pages[i].buffer), BLCKSZ);
+ 			memcpy(BufferGetPage(pages[i].buffer), tmp, BLCKSZ);
+ 
+ 			if (pages[i].fullImage)
+ 			{
+ 				/* Full page image doesn't require anything special from us */
+ 				XLogRegisterBuffer(i, pages[i].buffer, REGBUF_FORCE_IMAGE);
+ 			}
+ 			else
+ 			{
+ 				/*
+ 				 * In normal node calculate delta and write use it as data
+ 				 * associated with this page.
+ 				 */
+ 				XLogRegisterBuffer(i, pages[i].buffer, REGBUF_STANDARD);
+ 				writeDelta(&pages[i]);
+ 				XLogRegisterBufData(i, pages[i].data, pages[i].dataLen);
+ 			}
+ 		}
+ 
+ 		/* Insert xlog record */
+ 		lsn = XLogInsert(RM_GENERIC_ID, 0);
+ 
+ 		/* Set LSN and make buffers dirty */
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 			PageSetLSN(BufferGetPage(pages[i].buffer), lsn);
+ 			MarkBufferDirty(pages[i].buffer);
+ 		}
+ 		END_CRIT_SECTION();
+ 	}
+ 	else if (genericXlogStatus == GXLOG_UNLOGGED)
+ 	{
+ 		/* Unlogged relation: skip xlog-related stuff */
+ 		START_CRIT_SECTION();
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 			memcpy(BufferGetPage(pages[i].buffer), pages[i].image, BLCKSZ);
+ 			MarkBufferDirty(pages[i].buffer);
+ 		}
+ 		END_CRIT_SECTION();
+ 	}
+ 	else
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogFinish: generic xlog isn't started")));
+ 	}
+ 
+ 	genericXlogStatus = GXLOG_NOT_STARTED;
+ 
+ 	return lsn;
+ }
+ 
+ /*
+  * Abort generic xlog record.
+  */
+ void
+ GenericXLogAbort(void)
+ {
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogAbort: generic xlog isn't started")));
+ 
+ 	genericXlogStatus = GXLOG_NOT_STARTED;
+ }
+ 
+ /*
+  * Apply delta to given page image.
+  */
+ static void
+ applyPageRedo(Page page, Pointer data, Size dataSize)
+ {
+ 	Pointer ptr = data, end = data + dataSize;
+ 
+ 	while (ptr < end)
+ 	{
+ 		OffsetNumber	offset,
+ 						length;
+ 
+ 		memcpy(&offset, ptr, sizeof(offset));
+ 		ptr += sizeof(offset);
+ 		memcpy(&length, ptr, sizeof(length));
+ 		ptr += sizeof(length);
+ 
+ 		memcpy(page + offset, ptr, length);
+ 
+ 		ptr += length;
+ 	}
+ }
+ 
+ /*
+  * Redo function for generic xlog record.
+  */
+ void
+ generic_redo(XLogReaderState *record)
+ {
+ 	uint8		block_id;
+ 	Buffer		buffers[MAX_GENERIC_XLOG_PAGES] = {InvalidBuffer};
+ 	XLogRecPtr	lsn = record->EndRecPtr;
+ 
+ 	Assert(record->max_block_id < MAX_GENERIC_XLOG_PAGES);
+ 
+ 	/* Interate over blocks */
+ 	for (block_id = 0; block_id <= record->max_block_id; block_id++)
+ 	{
+ 		XLogRedoAction action;
+ 
+ 		if (!XLogRecHasBlockRef(record, block_id))
+ 			continue;
+ 
+ 		action = XLogReadBufferForRedo(record, block_id, &buffers[block_id]);
+ 
+ 		/* Apply redo to given block if needed */
+ 		if (action == BLK_NEEDS_REDO)
+ 		{
+ 			Pointer	blockData;
+ 			Size	blockDataSize;
+ 			Page	page;
+ 
+ 			page = BufferGetPage(buffers[block_id]);
+ 			blockData = XLogRecGetBlockData(record, block_id, &blockDataSize);
+ 			applyPageRedo(page, blockData, blockDataSize);
+ 
+ 			PageSetLSN(page, lsn);
+ 			MarkBufferDirty(buffers[block_id]);
+ 		}
+ 	}
+ 
+ 	/* Changes are done: unlock and release all buffers */
+ 	for (block_id = 0; block_id <= record->max_block_id; block_id++)
+ 	{
+ 		if (BufferIsValid(buffers[block_id]))
+ 			UnlockReleaseBuffer(buffers[block_id]);
+ 	}
+ }
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
new file mode 100644
index 7c4d773..7b38c16
*** a/src/backend/access/transam/rmgr.c
--- b/src/backend/access/transam/rmgr.c
***************
*** 11,16 ****
--- 11,17 ----
  #include "access/commit_ts.h"
  #include "access/gin.h"
  #include "access/gist_private.h"
+ #include "access/generic_xlog.h"
  #include "access/hash.h"
  #include "access/heapam_xlog.h"
  #include "access/brin_xlog.h"
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
new file mode 100644
index 88c3a49..2d69dc2
*** a/src/backend/replication/logical/decode.c
--- b/src/backend/replication/logical/decode.c
*************** LogicalDecodingProcessRecord(LogicalDeco
*** 135,140 ****
--- 135,141 ----
  		case RM_BRIN_ID:
  		case RM_COMMIT_TS_ID:
  		case RM_REPLORIGIN_ID:
+ 		case RM_GENERIC_ID:
  			break;
  		case RM_NEXT_ID:
  			elog(ERROR, "unexpected RM_NEXT_ID rmgr_id: %u", (RmgrIds) XLogRecGetRmid(buf.record));
diff --git a/src/bin/pg_xlogdump/.gitignore b/src/bin/pg_xlogdump/.gitignore
new file mode 100644
index eebaf30..33a1acf
*** a/src/bin/pg_xlogdump/.gitignore
--- b/src/bin/pg_xlogdump/.gitignore
***************
*** 4,9 ****
--- 4,10 ----
  /clogdesc.c
  /committsdesc.c
  /dbasedesc.c
+ /genericdesc.c
  /gindesc.c
  /gistdesc.c
  /hashdesc.c
diff --git a/src/bin/pg_xlogdump/rmgrdesc.c b/src/bin/pg_xlogdump/rmgrdesc.c
new file mode 100644
index f9cd395..cff7e59
*** a/src/bin/pg_xlogdump/rmgrdesc.c
--- b/src/bin/pg_xlogdump/rmgrdesc.c
***************
*** 11,16 ****
--- 11,17 ----
  #include "access/brin_xlog.h"
  #include "access/clog.h"
  #include "access/commit_ts.h"
+ #include "access/generic_xlog.h"
  #include "access/gin.h"
  #include "access/gist_private.h"
  #include "access/hash.h"
diff --git a/src/include/access/generic_xlog.h b/src/include/access/generic_xlog.h
new file mode 100644
index ...9a10c2c
*** a/src/include/access/generic_xlog.h
--- b/src/include/access/generic_xlog.h
***************
*** 0 ****
--- 1,92 ----
+ /*-------------------------------------------------------------------------
+  *
+  * generic_xlog.h
+  *	  Generic xlog API definition.
+  *
+  *
+  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/access/generic_xlog.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef GENERIC_XLOG_H
+ #define GENERIC_XLOG_H
+ 
+ #include "access/xlog.h"
+ #include "access/xlog_internal.h"
+ #include "storage/bufpage.h"
+ #include "utils/rel.h"
+ 
+ #define MAX_GENERIC_XLOG_PAGES	  3
+ 
+ /*-------------------------------------------------------------------------
+  * API for construction of generic xlog records
+  *
+  * This API allows user to construct generic xlog records which are
+  * describing difference between pages in general way.  Thus it's useful
+  * for extension which provides custom access methods because they couldn't
+  * register their own WAL redo routines.
+  *
+  * Generic xlog record should be constructed in following steps.
+  * 1) GenericXLogStart(relation) - start construction of generic xlog
+  *	  record for given relation.
+  * 2) GenericXLogRegister(buffer, isNew) - register one or more buffers
+  *	  for generic xlog record.  This function return a copy of page image
+  *	  where modifications should be performed.  The second argument
+  *	  indicates that block is new and full image should be taken.
+  * 3) Do modification of page images obtained in previous step.
+  * 4) GenericXLogFinish() - finish construction of generic xlog record.
+  *
+  * Please, note following points while constructing generic xlog records.
+  * - No direct modifications of page images are allowed! All modifications
+  *	 should be done in copies returned by GenericXLogRegister().  Literally
+  *	 code which makes generic xlog records should never call
+  *	 BufferGetPage() function.
+  * - On any step generic xlog record construction could be canceled by
+  *	 calling GenericXLogAbort().  All changes made in page images copies
+  *	 would be discarded.
+  * - Registrations of buffers (step 2) and modifications of page images
+  *	 (step 3) could be mixed in any sequence.  The only restriction is that
+  *	 you can modify page image only after registration of corresponding
+  *	 buffer.
+  * - After registration buffer also can be unregistered by calling
+  *	 GenericXLogUnregister(buffer).  In this case changes made in particular
+  *	 page image copy will be discarded.
+  * - Generic xlog assumes that pages are using standard layout.  I.e. all
+  *	 information between pd_lower and pd_upper will be discarded.
+  * - Maximum number of buffers simultaneously registered for generic xlog
+  *	 is MAX_GENERIC_XLOG_PAGES.  Error would be thrown if this limit
+  *	 exceeded.
+  * - Since you modify copies of page images, GenericXLogStart() doesn't
+  *	 start a critical section.  Thus, you can do memory allocation, error
+  *	 throwing etc between GenericXLogStart() and GenericXLogFinish().
+  *	 Actual critical section present inside GenericXLogFinish().
+  * - GenericXLogFinish() takes care about marking buffers dirty and setting
+  *	 their LSNs.  You don't need to do this explicitly.
+  * - For unlogged relations, everything work the same expect there is no
+  *	 WAL record produced.  Thus, you typically don't need to do any explicit
+  *	 checks for unlogged relations.
+  * - If registered buffer isn't new, generic xlog record contains delta
+  *	 between old and new page images.  This delta is produced by per byte
+  *	 comparison.  Current delta mechanist is not effective for data shift
+  *	 inside the page.  However, it could be improved in further versions.
+  * - Generic xlog redo function will acquire exclusive locks to buffers
+  *	 in the same order they were registered.  After redo of all changes
+  *	 locks would be released in the same order.  That could makes sense for
+  *	 concurrency.
+  *-------------------------------------------------------------------------
+  */
+ extern void GenericXLogStart(Relation relation);
+ extern Page GenericXLogRegister(Buffer buffer, bool isNew);
+ extern void GenericXLogUnregister(Buffer buffer);
+ extern XLogRecPtr GenericXLogFinish(void);
+ extern void GenericXLogAbort(void);
+ 
+ /* functions defined for rmgr */
+ extern void generic_redo(XLogReaderState *record);
+ extern const char *generic_identify(uint8 info);
+ extern void generic_desc(StringInfo buf, XLogReaderState *record);
+ 
+ #endif   /* GENERIC_XLOG_H */
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
new file mode 100644
index fab912d..3cfe6f7
*** a/src/include/access/rmgrlist.h
--- b/src/include/access/rmgrlist.h
*************** PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo
*** 45,47 ****
--- 45,48 ----
  PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL)
  PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL)
  PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL)
+ PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL)
bloom-contrib.6.patchapplication/octet-stream; name=bloom-contrib.6.patchDownload
diff --git a/contrib/Makefile b/contrib/Makefile
new file mode 100644
index bd251f6..3ac3818
*** a/contrib/Makefile
--- b/contrib/Makefile
*************** SUBDIRS = \
*** 8,13 ****
--- 8,14 ----
  		adminpack	\
  		auth_delay	\
  		auto_explain	\
+ 		bloom		\
  		btree_gin	\
  		btree_gist	\
  		chkpass		\
diff --git a/contrib/bloom/.gitignore b/contrib/bloom/.gitignore
new file mode 100644
index ...5dcb3ff
*** a/contrib/bloom/.gitignore
--- b/contrib/bloom/.gitignore
***************
*** 0 ****
--- 1,4 ----
+ # Generated subdirectories
+ /log/
+ /results/
+ /tmp_check/
diff --git a/contrib/bloom/Makefile b/contrib/bloom/Makefile
new file mode 100644
index ...f540027
*** a/contrib/bloom/Makefile
--- b/contrib/bloom/Makefile
***************
*** 0 ****
--- 1,21 ----
+ # contrib/bloom/Makefile
+ 
+ MODULE_big = bloom
+ OBJS = blcost.o blinsert.o blscan.o blutils.o blvacuum.o blvalidate.o $(WIN32RES)
+ 
+ EXTENSION = bloom
+ DATA = bloom--1.0.sql
+ PGFILEDESC = "bloom access method - signature file based index"
+ 
+ REGRESS = bloom
+ 
+ ifdef USE_PGXS
+ PG_CONFIG = pg_config
+ PGXS := $(shell $(PG_CONFIG) --pgxs)
+ include $(PGXS)
+ else
+ subdir = contrib/bloom
+ top_builddir = ../..
+ include $(top_builddir)/src/Makefile.global
+ include $(top_srcdir)/contrib/contrib-global.mk
+ endif
diff --git a/contrib/bloom/blcost.c b/contrib/bloom/blcost.c
new file mode 100644
index ...1265527
*** a/contrib/bloom/blcost.c
--- b/contrib/bloom/blcost.c
***************
*** 0 ****
--- 1,45 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blcost.c
+  *		Cost estimate function for bloom indexes.
+  *
+  * Copyright (c) 2015, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blcost.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "fmgr.h"
+ #include "optimizer/cost.h"
+ #include "utils/selfuncs.h"
+ 
+ #include "bloom.h"
+ 
+ void
+ blcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
+ 			   Cost *indexStartupCost, Cost *indexTotalCost,
+ 			   Selectivity *indexSelectivity, double *indexCorrelation)
+ {
+ 	IndexOptInfo   *index = path->indexinfo;
+ 	List		   *qinfos;
+ 	GenericCosts	costs;
+ 
+ 	/* Do preliminary analysis of indexquals */
+ 	qinfos = deconstruct_indexquals(path);
+ 
+ 	MemSet(&costs, 0, sizeof(costs));
+ 
+ 	/* We have to visit all index tuples anyway */
+ 	costs.numIndexTuples = index->tuples;
+ 
+ 	/* Use generic estimate */
+ 	genericcostestimate(root, path, loop_count, qinfos, &costs);
+ 
+ 	*indexStartupCost = costs.indexStartupCost;
+ 	*indexTotalCost = costs.indexTotalCost;
+ 	*indexSelectivity = costs.indexSelectivity;
+ 	*indexCorrelation = costs.indexCorrelation;
+ }
diff --git a/contrib/bloom/blinsert.c b/contrib/bloom/blinsert.c
new file mode 100644
index ...88356cf
*** a/contrib/bloom/blinsert.c
--- b/contrib/bloom/blinsert.c
***************
*** 0 ****
--- 1,276 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blinsert.c
+  *		Bloom index build and insert functions.
+  *
+  * Copyright (c) 2015, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blinsert.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "access/generic_xlog.h"
+ #include "catalog/index.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ 
+ #include "bloom.h"
+ 
+ PG_MODULE_MAGIC;
+ 
+ typedef struct
+ {
+ 	BloomState		blstate;
+ 	MemoryContext	tmpCtx;
+ 	char			data[BLCKSZ];
+ 	int64			count;
+ } BloomBuildState;
+ 
+ static void
+ flushBuildBuffer(Relation index, BloomBuildState *buildstate)
+ {
+ 	Page	page;
+ 	Buffer	buffer = BloomNewBuffer(index);
+ 
+ 	GenericXLogStart(index);
+ 	page = GenericXLogRegister(buffer, true);
+ 	memcpy(page, buildstate->data, BLCKSZ);
+ 	GenericXLogFinish();
+ 	UnlockReleaseBuffer(buffer);
+ }
+ 
+ static void
+ bloomBuildCallback(Relation index, HeapTuple htup, Datum *values,
+ 					bool *isnull, bool tupleIsAlive, void *state)
+ {
+ 	BloomBuildState	*buildstate = (BloomBuildState*)state;
+ 	MemoryContext	oldCtx;
+ 	BloomTuple		*itup;
+ 
+ 	oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
+ 
+ 	itup = BloomFormTuple(&buildstate->blstate, &htup->t_self, values, isnull);
+ 
+ 	if (BloomPageAddItem(&buildstate->blstate, buildstate->data, itup) == false)
+ 	{
+ 		flushBuildBuffer(index, buildstate);
+ 
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		memset(buildstate->data, 0, BLCKSZ);
+ 		BloomInitPage(buildstate->data, 0);
+ 		buildstate->count = 0;
+ 
+ 		if (BloomPageAddItem(&buildstate->blstate, buildstate->data, itup) == false)
+ 			elog(ERROR, "can not add new tuple"); /* should not be here! */
+ 	}
+ 	else
+ 	{
+ 		buildstate->count++;
+ 	}
+ 
+ 	MemoryContextSwitchTo(oldCtx);
+ 	MemoryContextReset(buildstate->tmpCtx);
+ }
+ 
+ IndexBuildResult *
+ blbuild(Relation heap, Relation index, IndexInfo *indexInfo)
+ {
+ 	IndexBuildResult   *result;
+ 	double				reltuples;
+ 	BloomBuildState		buildstate;
+ 	Buffer				metaBuffer;
+ 	Page				metaPage;
+ 
+ 	if (RelationGetNumberOfBlocks(index) != 0)
+ 		elog(ERROR, "index \"%s\" already contains data",
+ 			RelationGetRelationName(index));
+ 
+ 	/* initialize the meta page */
+ 	metaBuffer = BloomNewBuffer(index);
+ 	GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(metaBuffer, true);
+ 	BloomInitMetapage(metaPage, index);
+ 	GenericXLogFinish();
+ 	UnlockReleaseBuffer(metaBuffer);
+ 
+ 	initBloomState(&buildstate.blstate, index);
+ 
+ 	buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
+ 												"Bloom build temporary context",
+ 												ALLOCSET_DEFAULT_MINSIZE,
+ 												ALLOCSET_DEFAULT_INITSIZE,
+ 												ALLOCSET_DEFAULT_MAXSIZE);
+ 
+ 	memset(buildstate.data, 0, BLCKSZ);
+ 	BloomInitPage(buildstate.data, 0);
+ 	buildstate.count = 0;
+ 
+ 	reltuples = IndexBuildHeapScan(heap, index, indexInfo, true,
+ 									bloomBuildCallback, (void *) &buildstate);
+ 
+ 	if (buildstate.count > 0)
+ 		flushBuildBuffer(index, &buildstate);
+ 
+ 	MemoryContextDelete(buildstate.tmpCtx);
+ 
+ 	result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
+ 	result->heap_tuples = result->index_tuples = reltuples;
+ 
+ 	return result;
+ }
+ 
+ void
+ blbuildempty(Relation index)
+ {
+ 	Buffer		metaBuffer;
+ 	Page		metaPage;
+ 
+ 	if (RelationGetNumberOfBlocks(index) != 0)
+ 		elog(ERROR, "index \"%s\" already contains data",
+ 			RelationGetRelationName(index));
+ 
+ 	/* initialize the meta page */
+ 	metaBuffer = BloomNewBuffer(index);
+ 
+ 	GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(metaBuffer, true);
+ 	BloomInitMetapage(metaPage, index);
+ 	GenericXLogFinish();
+ 
+ 	UnlockReleaseBuffer(metaBuffer);
+ }
+ 
+ bool
+ blinsert(Relation index, Datum *values, bool *isnull,
+ 		ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique)
+ {
+ 	BloomState			blstate;
+ 	BloomTuple		   *itup;
+ 	MemoryContext		oldCtx;
+ 	MemoryContext		insertCtx;
+ 	BloomMetaPageData  *metaData;
+ 	Buffer				buffer,
+ 						metaBuffer;
+ 	Page				page,
+ 						metaPage;
+ 	BlockNumber			blkno = InvalidBlockNumber;
+ 	OffsetNumber		nStart;
+ 
+ 	insertCtx = AllocSetContextCreate(CurrentMemoryContext,
+ 										"Bloom insert temporary context",
+ 										ALLOCSET_DEFAULT_MINSIZE,
+ 										ALLOCSET_DEFAULT_INITSIZE,
+ 										ALLOCSET_DEFAULT_MAXSIZE);
+ 
+ 	oldCtx = MemoryContextSwitchTo(insertCtx);
+ 
+ 	initBloomState(&blstate, index);
+ 	itup = BloomFormTuple(&blstate, ht_ctid, values, isnull);
+ 
+ 	metaBuffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 	LockBuffer(metaBuffer, BUFFER_LOCK_SHARE);
+ 	metaData = BloomPageGetMeta(BufferGetPage(metaBuffer));
+ 
+ 	if (metaData->nEnd > metaData->nStart)
+ 	{
+ 		Page	page;
+ 
+ 		blkno = metaData->notFullPage[ metaData->nStart ];
+ 
+ 		Assert(blkno != InvalidBlockNumber);
+ 		LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		GenericXLogStart(index);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		if (BloomPageAddItem(&blstate, page, itup))
+ 		{
+ 			GenericXLogFinish();
+ 			UnlockReleaseBuffer(buffer);
+ 			ReleaseBuffer(metaBuffer);
+ 			goto away;
+ 		}
+ 		else
+ 		{
+ 			GenericXLogAbort();
+ 			UnlockReleaseBuffer(buffer);
+ 		}
+ 	}
+ 	else
+ 	{
+ 		/* no avaliable pages */
+ 		LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
+ 	}
+ 
+ 	LockBuffer(metaBuffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 	GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(metaBuffer, false);
+ 	metaData = BloomPageGetMeta(metaPage);
+ 
+ 	nStart = metaData->nStart;
+ 	if (metaData->nEnd > nStart &&
+ 		blkno == metaData->notFullPage[nStart] )
+ 		nStart++;
+ 
+ 	while (metaData->nEnd > nStart)
+ 	{
+ 		blkno = metaData->notFullPage[nStart];
+ 		Assert(blkno != InvalidBlockNumber);
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		if (BloomPageAddItem(&blstate, page, itup))
+ 		{
+ 			metaData->nStart = nStart;
+ 			GenericXLogFinish();
+ 			UnlockReleaseBuffer(buffer);
+ 			UnlockReleaseBuffer(metaBuffer);
+ 			goto away;
+ 		}
+ 		else
+ 		{
+ 			GenericXLogUnregister(buffer);
+ 			UnlockReleaseBuffer(buffer);
+ 		}
+ 		nStart++;
+ 	}
+ 
+ 	GenericXLogAbort();
+ 
+ 	buffer = BloomNewBuffer(index);
+ 
+ 	GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(metaBuffer, false);
+ 	metaData = BloomPageGetMeta(metaPage);
+ 	page = GenericXLogRegister(buffer, true);
+ 	BloomInitPage(page, 0);
+ 	BloomPageAddItem(&blstate, page, itup);
+ 
+ 	metaData->nStart = 0;
+ 	metaData->nEnd = 1;
+ 	metaData->notFullPage[ 0 ] = BufferGetBlockNumber(buffer);
+ 
+ 	GenericXLogFinish();
+ 
+ 	UnlockReleaseBuffer(buffer);
+ 	UnlockReleaseBuffer(metaBuffer);
+ 
+ away:
+ 	MemoryContextSwitchTo(oldCtx);
+ 	MemoryContextDelete(insertCtx);
+ 
+ 	return false;
+ }
diff --git a/contrib/bloom/bloom--1.0.sql b/contrib/bloom/bloom--1.0.sql
new file mode 100644
index ...7fa7513
*** a/contrib/bloom/bloom--1.0.sql
--- b/contrib/bloom/bloom--1.0.sql
***************
*** 0 ****
--- 1,19 ----
+ CREATE OR REPLACE FUNCTION blhandler(internal)
+ RETURNS index_am_handler
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C;
+ 
+ -- Access method
+ CREATE ACCESS METHOD bloom TYPE INDEX HANDLER blhandler;
+ 
+ -- Opclasses
+ 
+ CREATE OPERATOR CLASS int4_ops
+ DEFAULT FOR TYPE int4 USING bloom AS
+ 	OPERATOR	1	=(int4, int4),
+ 	FUNCTION	1	hashint4(int4);
+ 
+ CREATE OPERATOR CLASS text_ops
+ DEFAULT FOR TYPE text USING bloom AS
+ 	OPERATOR	1	=(text, text),
+ 	FUNCTION	1	hashtext(text);
diff --git a/contrib/bloom/bloom.control b/contrib/bloom/bloom.control
new file mode 100644
index ...4d4124b
*** a/contrib/bloom/bloom.control
--- b/contrib/bloom/bloom.control
***************
*** 0 ****
--- 1,5 ----
+ # bloom extension
+ comment = 'bloom access method - signature file based index'
+ default_version = '1.0'
+ module_pathname = '$libdir/bloom'
+ relocatable = true
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
new file mode 100644
index ...92a9b0f
*** a/contrib/bloom/bloom.h
--- b/contrib/bloom/bloom.h
***************
*** 0 ****
--- 1,162 ----
+ #ifndef _BLOOM_H_
+ #define _BLOOM_H_
+ 
+ #include "access/amapi.h"
+ #include "access/generic_xlog.h"
+ #include "access/itup.h"
+ #include "access/xlog.h"
+ #include "nodes/relation.h"
+ #include "fmgr.h"
+ 
+ #define	BLOOM_HASH_PROC			1
+ #define	BLOOM_NPROC				1
+ 
+ #define	BLOOM_EQUAL_STRATEGY	1
+ #define	BLOOM_NSTRATEGIES		1
+ 
+ typedef struct BloomPageOpaqueData
+ {
+ 	OffsetNumber	maxoff;
+ 	uint16			flags;
+ } BloomPageOpaqueData;
+ 
+ typedef BloomPageOpaqueData *BloomPageOpaque;
+ 
+ #define BLOOM_META		(1<<0)
+ #define BLOOM_DELETED	(2<<0)
+ 
+ #define BloomPageGetOpaque(page) ((BloomPageOpaque) PageGetSpecialPointer(page))
+ #define BloomPageGetMaxOffset(page) (BloomPageGetOpaque(page)->maxoff)
+ #define BloomPageIsMeta(page) (BloomPageGetOpaque(page)->flags & BLOOM_META)
+ #define BloomPageIsDeleted(page) (BloomPageGetOpaque(page)->flags & BLOOM_DELETED)
+ #define BloomPageSetDeleted(page) (BloomPageGetOpaque(page)->flags |= BLOOM_DELETED)
+ #define BloomPageSetNonDeleted(page) (BloomPageGetOpaque(page)->flags &= ~BLOOM_DELETED)
+ #define BloomPageGetData(page)		((BloomTuple *)PageGetContents(page))
+ #define BloomPageGetTuple(state, page, offset) \
+ 	((BloomTuple *)(PageGetContents(page) \
+ 		+ (state)->sizeOfBloomTuple * ((offset) - 1)))
+ #define BloomPageGetNextTuple(state, tuple) \
+ 	((BloomTuple *)((Pointer)(tuple) + (state)->sizeOfBloomTuple))
+ 
+ #define BLOOM_METAPAGE_BLKNO  	(0)
+ #define BLOOM_HEAD_BLKNO  		(1)
+ 
+ typedef struct BloomOptions 
+ {
+ 	int32	vl_len_;	/* varlena header (do not touch directly!) */
+ 	int		bloomLength;	
+ 	int		bitSize[INDEX_MAX_KEYS];
+ } BloomOptions;
+ 
+ 
+ 
+ typedef BlockNumber FreeBlockNumberArray[
+ 			MAXALIGN_DOWN(
+ 				BLCKSZ - 
+ 					SizeOfPageHeaderData - 
+ 					MAXALIGN(sizeof(BloomPageOpaqueData)) - 
+ 					/* header of BloomMetaPageData struct */
+ 					MAXALIGN(sizeof(uint16) * 2 + sizeof(uint32) + sizeof(BloomOptions)) 
+ 			) / sizeof(BlockNumber) 
+ 		];
+ 
+ typedef struct BloomMetaPageData
+ {
+ 	uint32					magickNumber;
+ 	uint16					nStart;
+ 	uint16					nEnd;
+ 	BloomOptions			opts;
+ 	FreeBlockNumberArray	notFullPage;
+ } BloomMetaPageData;
+ 
+ #define BLOOM_MAGICK_NUMBER	(0xDBAC0DED)
+ 
+ #define BloomMetaBlockN		(sizeof(FreeBlockNumberArray) / sizeof(BlockNumber))
+ #define BloomPageGetMeta(p) \
+ 	((BloomMetaPageData *) PageGetContents(p))
+ 
+ typedef struct BloomState 
+ {
+ 	FmgrInfo			hashFn[INDEX_MAX_KEYS];
+ 	BloomOptions		*opts; /* stored in rd_amcache and defined at creation time */
+ 	int32				nColumns;
+ 	/* 
+ 	 * sizeOfBloomTuple is index's specific, and it depends on
+ 	 * reloptions, so precompute it
+ 	 */
+ 	int32				sizeOfBloomTuple; 
+ } BloomState;
+ 
+ #define BloomPageGetFreeSpace(state, page) \
+ 	(BLCKSZ - MAXALIGN(SizeOfPageHeaderData) \
+ 		- BloomPageGetMaxOffset(page) * (state)->sizeOfBloomTuple \
+ 		- MAXALIGN(sizeof(BloomPageOpaqueData)))
+ 
+ /*
+  * Tuples are very different from all other relations
+  */
+ typedef uint16	SignType;
+ 
+ typedef struct BloomTuple
+ {
+ 	ItemPointerData		heapPtr;
+ 	SignType			sign[1];
+ } BloomTuple;
+ 
+ #define BLOOMTUPLEHDRSZ	offsetof(BloomTuple, sign)
+ 
+ #define BITSIGNTYPE	(BITS_PER_BYTE * sizeof(SignType))
+ #define GETWORD(x,i) ( *( (SignType*)(x) + (int)( (i) / BITSIGNTYPE ) ) )
+ #define GETBITBYTE(x,i) ( ((SignType)(x)) >> i & 0x01 )
+ #define CLRBIT(x,i)   GETWORD(x,i) &= ~( 0x01 << ( (i) % BITSIGNTYPE ) )
+ #define SETBIT(x,i)   GETWORD(x,i) |=  ( 0x01 << ( (i) % BITSIGNTYPE ) )
+ #define GETBIT(x,i) ( (GETWORD(x,i) >> ( (i) % BITSIGNTYPE )) & 0x01 )
+ 
+ /* Opaque data structure for bloom index scan */
+ typedef struct BloomScanOpaqueData
+ {
+ 	SignType   *sign;	/* Scan signature */
+ 	BloomState	state;
+ } BloomScanOpaqueData;
+ 
+ typedef BloomScanOpaqueData *BloomScanOpaque;
+ 
+ /* blutils.c */
+ extern Datum blhandler(PG_FUNCTION_ARGS);
+ extern void initBloomState(BloomState *state, Relation index);
+ extern void BloomInitMetapage(Page page, Relation index);
+ extern void BloomInitPage(Page page, uint16 flags);
+ extern Buffer BloomNewBuffer(Relation index);
+ extern void signValue(BloomState *state, SignType *sign, Datum value, int attno);
+ extern BloomTuple* BloomFormTuple(BloomState *state, ItemPointer iptr, Datum *values, bool *isnull);
+ extern bool BloomPageAddItem(BloomState *state, Page page, BloomTuple *tuple);
+ 
+ /* blvalidate.c */
+ extern bool blvalidate(Oid opclassoid);
+ 
+ /* interface functions */
+ extern bool blinsert(Relation index, Datum *values, bool *isnull,
+ 					 ItemPointer ht_ctid, Relation heapRel,
+ 					 IndexUniqueCheck checkUnique);
+ extern IndexScanDesc blbeginscan(Relation r, int nkeys, int norderbys);
+ extern int64 blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
+ extern void blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ 					 ScanKey orderbys, int norderbys);
+ extern void blendscan(IndexScanDesc scan);
+ extern void blmarkpos(IndexScanDesc scan);
+ extern void blrestrpos(IndexScanDesc scan);
+ extern IndexBuildResult *blbuild(Relation heap, Relation index,
+ 								   struct IndexInfo *indexInfo);
+ extern void blbuildempty(Relation index);
+ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
+ 	IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback,
+ 	void *callback_state);
+ extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
+ 	IndexBulkDeleteResult *stats);
+ extern bytea *bloptions(Datum reloptions, bool validate);
+ extern void blcostestimate(PlannerInfo *root, IndexPath *path,
+ 						   double loop_count, Cost *indexStartupCost,
+ 						   Cost *indexTotalCost, Selectivity *indexSelectivity,
+ 						   double *indexCorrelation);
+ 
+ #endif
diff --git a/contrib/bloom/blscan.c b/contrib/bloom/blscan.c
new file mode 100644
index ...1670008
*** a/contrib/bloom/blscan.c
--- b/contrib/bloom/blscan.c
***************
*** 0 ****
--- 1,175 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blscan.c
+  *		Bloom index scan functions.
+  *
+  * Copyright (c) 2015, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blscan.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/relscan.h"
+ #include "pgstat.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/lmgr.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ 
+ #include "bloom.h"
+ 
+ IndexScanDesc
+ blbeginscan(Relation r, int nkeys, int norderbys)
+ {
+ 	IndexScanDesc scan;
+ 
+ 	scan = RelationGetIndexScan(r, nkeys, norderbys);
+ 
+ 	return scan;
+ }
+ 
+ void
+ blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ 							 ScanKey orderbys, int norderbys)
+ {
+ 	BloomScanOpaque so;
+ 
+ 	so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so == NULL)
+ 	{
+ 		/* if called from blbeginscan */
+ 		so = (BloomScanOpaque) palloc(sizeof(BloomScanOpaqueData));
+ 		initBloomState(&so->state, scan->indexRelation);
+ 		scan->opaque = so;
+ 
+ 	}
+ 	else
+ 	{
+ 		if (so->sign)
+ 			pfree(so->sign);
+ 	}
+ 	so->sign = NULL;
+ 
+ 	if (scankey && scan->numberOfKeys > 0)
+ 	{
+ 		memmove(scan->keyData, scankey,
+ 				scan->numberOfKeys * sizeof(ScanKeyData));
+ 	}
+ }
+ 
+ void
+ blendscan(IndexScanDesc scan)
+ {
+ 	BloomScanOpaque so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so->sign)
+ 		pfree(so->sign);
+ 	so->sign = NULL;
+ }
+ 
+ void
+ blmarkpos(IndexScanDesc scan)
+ {
+ 	elog(ERROR, "Bloom does not support mark/restore");
+ }
+ 
+ void
+ blrestrpos(IndexScanDesc scan)
+ {
+ 	elog(ERROR, "Bloom does not support mark/restore");
+ }
+ 
+ int64
+ blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
+ {
+ 	int64					ntids = 0;
+ 	BlockNumber				blkno = BLOOM_HEAD_BLKNO,
+ 							npages;
+ 	int						i;
+ 	BufferAccessStrategy	bas;
+ 	BloomScanOpaque 		so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so->sign == NULL && scan->numberOfKeys > 0)
+ 	{
+ 		/* New search: have to calculate search signature */
+ 		ScanKey skey = scan->keyData;
+ 
+ 		so->sign = palloc0(sizeof(SignType) * so->state.opts->bloomLength); 
+ 		
+ 		for(i = 0; i < scan->numberOfKeys; i++)
+ 		{
+ 			/*
+ 			 * Assume, that Bloom-indexable operators are strict, so nothing could
+ 			 * be found
+ 			 */
+ 
+ 			if (skey->sk_flags & SK_ISNULL)
+ 			{
+ 				pfree(so->sign);
+ 				so->sign = NULL;
+ 				return 0;
+ 			}
+ 
+ 			/* Add next value to the signature */
+ 			signValue(&so->state, so->sign, skey->sk_argument,
+ 															skey->sk_attno - 1);
+ 
+ 			skey++;
+ 		}
+ 	}
+ 
+ 	/*
+ 	 * We're going to read the whole index. This is why we use appropriate
+ 	 * buffer access strategy.
+ 	 */
+ 	bas = GetAccessStrategy(BAS_BULKREAD);
+ 	npages = RelationGetNumberOfBlocks(scan->indexRelation);
+ 
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		Buffer 			buffer;
+ 		Page			page;
+ 
+ 		buffer = ReadBufferExtended(scan->indexRelation, MAIN_FORKNUM,
+ 									blkno, RBM_NORMAL, bas);
+ 
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 		page = BufferGetPage(buffer);
+ 
+ 		if (!BloomPageIsDeleted(page))
+ 		{
+ 			OffsetNumber offset, maxOffset = BloomPageGetMaxOffset(page);
+ 
+ 			for (offset = 1; offset <= maxOffset; offset++)
+ 			{
+ 				BloomTuple *itup = BloomPageGetTuple(&so->state, page, offset);
+ 				bool		res = true;
+ 
+ 				/* Check index signature with scan signature */
+ 				for (i = 0; res && i < so->state.opts->bloomLength; i++)
+ 				{
+ 					if ((itup->sign[i] & so->sign[i]) != so->sign[i])
+ 						res = false;
+ 				}
+ 
+ 				/* Add matching tuples to bitmap */
+ 				if (res)
+ 				{
+ 					tbm_add_tuples(tbm, &itup->heapPtr, 1, true);
+ 					ntids++;
+ 				}
+ 			}
+ 		}
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 		CHECK_FOR_INTERRUPTS();
+ 	}
+ 	FreeAccessStrategy(bas);
+ 
+ 	return ntids;
+ }
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
new file mode 100644
index ...352b352
*** a/contrib/bloom/blutils.c
--- b/contrib/bloom/blutils.c
***************
*** 0 ****
--- 1,363 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blscan.c
+  *		Bloom index scan functions.
+  *
+  * Copyright (c) 2015, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blscan.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/amapi.h"
+ #include "access/generic_xlog.h"
+ #include "catalog/index.h"
+ #include "storage/lmgr.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "utils/memutils.h"
+ #include "access/reloptions.h"
+ #include "storage/freespace.h"
+ #include "storage/indexfsm.h"
+ 
+ #include "bloom.h"
+ 
+ PG_FUNCTION_INFO_V1(blhandler);
+ 
+ /*
+  * Bloom handler function: return IndexAmRoutine with access method parameters
+  * and callbacks.
+  */
+ Datum
+ blhandler(PG_FUNCTION_ARGS)
+ {
+ 	IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
+ 
+ 	amroutine->amstrategies = 1;
+ 	amroutine->amsupport = 1;
+ 	amroutine->amcanorder = false;
+ 	amroutine->amcanorderbyop = false;
+ 	amroutine->amcanbackward = false;
+ 	amroutine->amcanunique = false;
+ 	amroutine->amcanmulticol = true;
+ 	amroutine->amoptionalkey = true;
+ 	amroutine->amsearcharray = false;
+ 	amroutine->amsearchnulls = false;
+ 	amroutine->amstorage = false;
+ 	amroutine->amclusterable = false;
+ 	amroutine->ampredlocks = false;
+ 	amroutine->amkeytype = 0;
+ 
+ 	amroutine->aminsert = blinsert;
+ 	amroutine->ambeginscan = blbeginscan;
+ 	amroutine->amgettuple = NULL;
+ 	amroutine->amgetbitmap = blgetbitmap;
+ 	amroutine->amrescan = blrescan;
+ 	amroutine->amendscan = blendscan;
+ 	amroutine->ammarkpos = blmarkpos;
+ 	amroutine->amrestrpos = blrestrpos;
+ 	amroutine->ambuild = blbuild;
+ 	amroutine->ambuildempty = blbuildempty;
+ 	amroutine->ambulkdelete = blbulkdelete;
+ 	amroutine->amvacuumcleanup = blvacuumcleanup;
+ 	amroutine->amcanreturn = NULL;
+ 	amroutine->amcostestimate = blcostestimate;
+ 	amroutine->amoptions = bloptions;
+ 	amroutine->amvalidate = blvalidate;
+ 
+ 	PG_RETURN_POINTER(amroutine);
+ }
+ 
+ void 
+ initBloomState(BloomState *state, Relation index)
+ {
+ 	int	i;
+ 
+ 	state->nColumns = index->rd_att->natts;
+ 
+ 	/* Initialize hash function for each attribute */
+ 	for (i = 0; i < index->rd_att->natts; i++)
+ 	{
+ 		fmgr_info_copy(&(state->hashFn[i]),
+ 						index_getprocinfo(index, i + 1, BLOOM_HASH_PROC),
+ 						CurrentMemoryContext);
+ 	}
+ 
+ 	/* Inititalize amcache if needed */
+ 	if (!index->rd_amcache)
+ 	{
+ 		Buffer				buffer;
+ 		Page				page;
+ 		BloomMetaPageData	*meta;
+ 		BloomOptions		*opts;
+ 		BloomPageOpaque		opaque;
+ 
+ 		opts = MemoryContextAlloc(index->rd_indexcxt, sizeof(BloomOptions));
+ 
+ 		buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 
+ 		page = BufferGetPage(buffer);
+ 		opaque = BloomPageGetOpaque(page);
+ 
+ 		if (!BloomPageIsMeta(page))
+ 			elog(ERROR, "Relation is not a bloom index");
+ 		meta = BloomPageGetMeta(BufferGetPage(buffer));
+ 
+ 		if (meta->magickNumber != BLOOM_MAGICK_NUMBER)
+ 			elog(ERROR, "Relation is not a bloom index");
+ 
+ 		*opts = meta->opts;
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 
+ 		index->rd_amcache = (void *)opts;
+ 	}
+ 
+ 	state->opts = (BloomOptions *)index->rd_amcache;
+ 	state->sizeOfBloomTuple = BLOOMTUPLEHDRSZ +
+ 									sizeof(SignType) * state->opts->bloomLength; 
+ }
+ 
+ /*
+  * Add bits of given value to the signature.
+  */
+ void
+ signValue(BloomState *state, SignType *sign, Datum value, int attno)
+ {
+ 	uint32		hashVal;
+ 	int 		nBit, j;
+ 
+ 	/*
+ 	 * init generator with "column's" number to get
+ 	 * "hashed" seed for new value. We don't want to map
+ 	 * the same numbers from different columns into the same bits!
+ 	 */
+ 	srand(attno);
+ 
+ 	/*
+ 	 * Init hash sequence to map our value into bits. the same values
+ 	 * in different columns will be mapped into different bits because
+ 	 * of step above
+ 	 */
+ 	hashVal = DatumGetInt32(FunctionCall1(&state->hashFn[attno], value));
+ 	srand(hashVal ^ rand());
+ 
+ 	for (j = 0; j < state->opts->bitSize[attno]; j++)
+ 	{
+ 		/* prevent mutiple evaluation */
+ 		nBit = rand() % (state->opts->bloomLength * BITSIGNTYPE); 
+ 		SETBIT(sign, nBit);
+ 	}
+ }
+ 
+ /*
+  * Make bloom tuple from values.
+  */
+ BloomTuple *
+ BloomFormTuple(BloomState *state, ItemPointer iptr, Datum *values, bool *isnull)
+ {
+ 	int 		i;
+ 	BloomTuple *res = (BloomTuple *)palloc0(state->sizeOfBloomTuple);
+ 
+ 	res->heapPtr = *iptr;
+ 
+     /*
+ 	 * Blooming
+ 	 */
+ 	for (i = 0; i < state->nColumns; i++)
+ 	{
+ 		/* skip nulls */
+ 		if (isnull[i])
+ 			continue;
+ 
+ 		signValue(state, res->sign, values[i], i);
+ 	}
+ 
+ 	return res;
+ }
+ 
+ /*
+  * Add new bloom tuple to the page.
+  */
+ bool
+ BloomPageAddItem(BloomState *state, Page page, BloomTuple *tuple)
+ {
+ 	BloomTuple		   *itup;
+ 	BloomPageOpaque		opaque;
+ 	Pointer				ptr;
+ 
+ 	if (BloomPageGetFreeSpace(state, page) < state->sizeOfBloomTuple)
+ 		return false;
+ 
+ 	/* Copy new tuple to the end of page */
+ 	opaque = BloomPageGetOpaque(page);
+ 	itup = BloomPageGetTuple(state, page, opaque->maxoff + 1);
+ 	memcpy((Pointer)itup, (Pointer)tuple, state->sizeOfBloomTuple);
+ 
+ 	/* Adjust maxoff and pd_lower */
+ 	opaque->maxoff++;
+ 	ptr = (Pointer)BloomPageGetTuple(state, page, opaque->maxoff + 1);
+ 	((PageHeader) page)->pd_lower = ptr - page;
+ 
+ 	return true;
+ }
+ 
+ /*
+  * Allocate a new page (either by recycling, or by extending the index file)
+  * The returned buffer is already pinned and exclusive-locked
+  * Caller is responsible for initializing the page by calling BloomInitBuffer
+  */
+ Buffer
+ BloomNewBuffer(Relation index)
+ {
+ 	Buffer      buffer;
+ 	bool        needLock;
+ 
+ 	/* First, try to get a page from FSM */
+ 	for (;;)
+ 	{
+ 		BlockNumber blkno = GetFreeIndexPage(index);
+ 
+ 		if (blkno == InvalidBlockNumber)
+ 			break;
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 
+ 		/*
+ 		 * We have to guard against the possibility that someone else already
+ 		 * recycled this page; the buffer may be locked if so.
+ 		 */
+ 		if (ConditionalLockBuffer(buffer))
+ 		{
+ 			Page        page = BufferGetPage(buffer);
+ 
+ 			if (PageIsNew(page))
+ 				return buffer;  /* OK to use, if never initialized */
+ 
+ 			if (BloomPageIsDeleted(page))
+ 				return buffer;  /* OK to use */
+ 
+ 			LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ 		}
+ 
+ 		/* Can't use it, so release buffer and try again */
+ 		ReleaseBuffer(buffer);
+ 	}
+ 
+ 	/* Must extend the file */
+ 	needLock = !RELATION_IS_LOCAL(index);
+ 	if (needLock)
+ 		LockRelationForExtension(index, ExclusiveLock);
+ 
+ 	buffer = ReadBuffer(index, P_NEW);
+ 	LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 	if (needLock)
+ 		UnlockRelationForExtension(index, ExclusiveLock);
+ 
+ 	return buffer;
+ }
+ 
+ void
+ BloomInitPage(Page page, uint16 flags)
+ {
+ 	BloomPageOpaque opaque;
+ 
+ 	PageInit(page, BLCKSZ, sizeof(BloomPageOpaqueData));
+ 
+ 	opaque = BloomPageGetOpaque(page);
+ 	memset(opaque, 0, sizeof(BloomPageOpaqueData));
+ 	opaque->maxoff = 0;
+ 	opaque->flags = flags;
+ }
+ 
+ static BloomOptions *
+ makeDefaultBloomOptions(BloomOptions *opts)
+ {
+ 	int i;
+ 
+ 	if (!opts)
+ 		opts = palloc0(sizeof(BloomOptions));
+ 
+ 	if (opts->bloomLength <= 0)
+ 		opts->bloomLength = 5;
+ 
+ 	for(i = 0; i < INDEX_MAX_KEYS; i++)
+ 		if (opts->bitSize[i] <= 0
+ 				|| opts->bitSize[i] >= opts->bloomLength * sizeof(SignType))
+ 			opts->bitSize[i] = 2;
+ 
+ 	return opts;
+ }
+ 
+ void
+ BloomInitMetapage(Page page, Relation index)
+ {
+ 	BloomMetaPageData	*metadata;
+ 
+ 	BloomInitPage(page, BLOOM_META);
+ 	metadata = BloomPageGetMeta(page);
+ 	memset(metadata, 0, sizeof(BloomMetaPageData));
+ 	metadata->magickNumber = BLOOM_MAGICK_NUMBER;
+ 	metadata->opts = *makeDefaultBloomOptions((BloomOptions*)index->rd_options);
+ 	((PageHeader) page)->pd_lower += sizeof(BloomMetaPageData);
+ }
+ 
+ static relopt_kind bloom_kind = 0;
+ 
+ void _PG_init(void);
+ void 
+ _PG_init(void)
+ {
+ 	int i;
+ 	char				buf[16];
+ 
+ 	bloom_kind = add_reloption_kind();
+ 
+ 	add_int_reloption(bloom_kind, "length", "Length of signature in uint16 type",
+ 						5, 1, 256);
+ 
+ 	for(i = 0; i < INDEX_MAX_KEYS;i ++)
+ 	{
+ 		snprintf(buf, 16, "col%d", i+1);
+ 		add_int_reloption(bloom_kind, buf, "Number of bits for corresponding column",
+ 								2, 1, 2048);
+ 	}
+ }
+ 
+ bytea *
+ bloptions(Datum reloptions, bool validate)
+ {
+ 	relopt_value	   *options;
+ 	int					numoptions;
+ 	BloomOptions	   *rdopts;
+ 	relopt_parse_elt	tab[INDEX_MAX_KEYS + 1];
+ 	int					i;
+ 	char				buf[16];
+ 
+ 	tab[0].optname = "length";
+ 	tab[0].opttype = RELOPT_TYPE_INT;
+ 	tab[0].offset = offsetof(BloomOptions, bloomLength);
+ 
+ 	for(i = 0; i < INDEX_MAX_KEYS; i++)
+ 	{
+ 		snprintf(buf, sizeof(buf), "col%d", i + 1);
+ 		tab[i + 1].optname = pstrdup(buf);
+ 		tab[i + 1].opttype = RELOPT_TYPE_INT;
+ 		tab[i + 1].offset = offsetof(BloomOptions, bitSize[i]);
+ 	}
+ 
+ 	options = parseRelOptions(reloptions, validate, bloom_kind, &numoptions);
+ 	rdopts = allocateReloptStruct(sizeof(BloomOptions), options, numoptions);
+ 	fillRelOptions((void *) rdopts, sizeof(BloomOptions), options, numoptions,
+ 						validate, tab, INDEX_MAX_KEYS + 1);
+ 		
+ 	rdopts = makeDefaultBloomOptions(rdopts);
+ 
+ 	return (bytea *)rdopts;
+ }
diff --git a/contrib/bloom/blvacuum.c b/contrib/bloom/blvacuum.c
new file mode 100644
index ...f0f2fe1
*** a/contrib/bloom/blvacuum.c
--- b/contrib/bloom/blvacuum.c
***************
*** 0 ****
--- 1,183 ----
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "catalog/storage.h"
+ #include "commands/vacuum.h"
+ #include "miscadmin.h"
+ #include "postmaster/autovacuum.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "storage/lmgr.h"
+ 
+ #include "bloom.h"
+ 
+ IndexBulkDeleteResult *
+ blbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
+ 			 IndexBulkDeleteCallback callback, void *callback_state)
+ {
+ 	Relation				index = info->index;
+ 	BlockNumber				blkno,
+ 							npages;
+ 	FreeBlockNumberArray	notFullPage;
+ 	int						countPage = 0;
+ 	BloomState				state;
+ 	Buffer					buffer;
+ 	Page					page;
+ 
+ 	if (stats == NULL)
+ 		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+ 
+ 	initBloomState(&state, index); 
+ 
+ 	/*
+ 	 * Interate over the pages. We don't care about concurrently added pages,
+ 	 * they can't contain tuples to delete.
+ 	 */
+ 	npages = RelationGetNumberOfBlocks(index);
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		BloomTuple *itup, *itupPtr, *itupEnd;
+ 
+ 		buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
+ 									RBM_NORMAL, info->strategy);
+ 
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		GenericXLogStart(index);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		if (BloomPageIsDeleted(page))
+ 		{
+ 			UnlockReleaseBuffer(buffer);
+ 			CHECK_FOR_INTERRUPTS();
+ 			continue;
+ 		}
+ 
+ 		/* Iterate over the tuples */
+ 		itup = BloomPageGetTuple(&state, page, 1);
+ 		itupPtr = BloomPageGetTuple(&state, page, 1);
+ 		itupEnd = BloomPageGetTuple(&state, page, BloomPageGetMaxOffset(page) + 1);
+ 		while (itup < itupEnd)
+ 		{
+ 			/* Do we have to delete this tuple? */
+ 			if (callback(&itup->heapPtr, callback_state))
+ 			{
+ 				stats->tuples_removed += 1;
+ 				BloomPageGetOpaque(page)->maxoff--;
+ 			} 
+ 			else 
+ 			{
+ 				if (itupPtr != itup)
+ 				{
+ 					/*
+ 					 * If we already delete something before, we have to move
+ 					 * this tuple backward.
+ 					 */
+ 					memmove((Pointer)itupPtr, (Pointer)itup,
+ 							state.sizeOfBloomTuple);
+ 				}
+ 				stats->num_index_tuples++;
+ 				itupPtr = BloomPageGetNextTuple(&state, itupPtr);
+ 			}
+ 
+ 			itup = BloomPageGetNextTuple(&state, itup);
+ 		}
+ 
+ 		/* Did we delete something? */
+ 		if (itupPtr != itup)
+ 		{
+ 			/* Is it empty page now? */
+ 			if (itupPtr == BloomPageGetData(page))
+ 				BloomPageSetDeleted(page);
+ 			/* Adjust pg_lower */
+ 			((PageHeader) page)->pd_lower = (Pointer)itupPtr - page;
+ 			/* Finish WAL-logging */
+ 			GenericXLogFinish();
+ 		}
+ 		else
+ 		{
+ 			/* Didn't change anything: abort WAL-logging */
+ 			GenericXLogAbort();
+ 		}
+ 
+ 		if (!BloomPageIsDeleted(page) && 
+ 				BloomPageGetFreeSpace(&state, page) > state.sizeOfBloomTuple && 
+ 				countPage < BloomMetaBlockN)
+ 			notFullPage[countPage++] = blkno;
+ 		UnlockReleaseBuffer(buffer);
+ 		CHECK_FOR_INTERRUPTS();
+ 	}
+ 
+ 	if (countPage > 0)
+ 	{
+ 		BloomMetaPageData	*metaData;
+ 
+ 		buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 		GenericXLogStart(index);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		metaData = BloomPageGetMeta(page);
+ 		memcpy(metaData->notFullPage, notFullPage, sizeof(FreeBlockNumberArray));
+ 		metaData->nStart=0;
+ 		metaData->nEnd = countPage;
+ 
+ 		GenericXLogFinish();
+ 		UnlockReleaseBuffer(buffer);
+ 	}
+ 
+ 	return stats;
+ }
+ 
+ IndexBulkDeleteResult *
+ blvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
+ {
+ 	Relation	index = info->index;
+ 	BlockNumber npages,
+ 				blkno;
+ 	BlockNumber	totFreePages;
+ 
+ 	if (info->analyze_only)
+ 		return stats;
+ 
+ 	if (stats == NULL)
+ 		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+ 
+ 	/*
+ 	 * Iterate over the pages: insert deleted pages into FSM and collect
+ 	 * statistics.
+ 	 */
+ 	npages = RelationGetNumberOfBlocks(index);
+ 	totFreePages = 0;
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		Buffer		buffer;
+ 		Page		page;
+ 
+ 		vacuum_delay_point();
+ 
+ 		buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
+ 									RBM_NORMAL, info->strategy);
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 		page = (Page) BufferGetPage(buffer);
+ 																						
+ 		if (BloomPageIsDeleted(page))
+ 		{
+ 			RecordFreeIndexPage(index, blkno);
+ 			totFreePages++;
+ 		}
+ 		else
+ 		{
+ 			stats->num_index_tuples += BloomPageGetMaxOffset(page);
+ 			stats->estimated_count += BloomPageGetMaxOffset(page);
+ 		}
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 	}
+ 
+ 	IndexFreeSpaceMapVacuum(info->index);
+ 	stats->pages_free = totFreePages;
+ 	stats->num_pages = RelationGetNumberOfBlocks(index);
+ 
+ 	return stats;
+ }
diff --git a/contrib/bloom/blvalidate.c b/contrib/bloom/blvalidate.c
new file mode 100644
index ...677413a
*** a/contrib/bloom/blvalidate.c
--- b/contrib/bloom/blvalidate.c
***************
*** 0 ****
--- 1,221 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blvalidate.c
+  *	  Opclass validator for bloom.
+  *
+  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blvalidate.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/amvalidate.h"
+ #include "access/htup_details.h"
+ #include "catalog/pg_amop.h"
+ #include "catalog/pg_amproc.h"
+ #include "catalog/pg_opclass.h"
+ #include "catalog/pg_opfamily.h"
+ #include "catalog/pg_type.h"
+ #include "utils/builtins.h"
+ #include "utils/lsyscache.h"
+ #include "utils/syscache.h"
+ 
+ #include "bloom.h"
+ 
+ /*
+  * Validator for a bloom opclass.
+  */
+ bool
+ blvalidate(Oid opclassoid)
+ {
+ 	bool		result = true;
+ 	HeapTuple	classtup;
+ 	Form_pg_opclass classform;
+ 	Oid			opfamilyoid;
+ 	Oid			opcintype;
+ 	Oid			opckeytype;
+ 	char	   *opclassname;
+ 	HeapTuple	familytup;
+ 	Form_pg_opfamily familyform;
+ 	char	   *opfamilyname;
+ 	CatCList   *proclist,
+ 			   *oprlist;
+ 	List	   *grouplist;
+ 	OpFamilyOpFuncGroup *opclassgroup;
+ 	int			i;
+ 	ListCell   *lc;
+ 
+ 	/* Fetch opclass information */
+ 	classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
+ 	if (!HeapTupleIsValid(classtup))
+ 		elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
+ 	classform = (Form_pg_opclass) GETSTRUCT(classtup);
+ 
+ 	opfamilyoid = classform->opcfamily;
+ 	opcintype = classform->opcintype;
+ 	opckeytype = classform->opckeytype;
+ 	if (!OidIsValid(opckeytype))
+ 		opckeytype = opcintype;
+ 	opclassname = NameStr(classform->opcname);
+ 
+ 	/* Fetch opfamily information */
+ 	familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
+ 	if (!HeapTupleIsValid(familytup))
+ 		elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
+ 	familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
+ 
+ 	opfamilyname = NameStr(familyform->opfname);
+ 
+ 	/* Fetch all operators and support functions of the opfamily */
+ 	oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
+ 	proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
+ 
+ 	/* Check individual support functions */
+ 	for (i = 0; i < proclist->n_members; i++)
+ 	{
+ 		HeapTuple	proctup = &proclist->members[i]->tuple;
+ 		Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
+ 		bool		ok;
+ 
+ 		/*
+ 		 * All bloom support functions should be registered with matching
+ 		 * left/right types
+ 		 */
+ 		if (procform->amproclefttype != procform->amprocrighttype)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains support procedure %s with cross-type registration",
+ 							opfamilyname,
+ 							format_procedure(procform->amproc))));
+ 			result = false;
+ 		}
+ 
+ 		/*
+ 		 * We can't check signatures except within the specific opclass, since
+ 		 * we need to know the associated opckeytype in many cases.
+ 		 */
+ 		if (procform->amproclefttype != opcintype)
+ 			continue;
+ 
+ 		/* Check procedure numbers and function signatures */
+ 		switch (procform->amprocnum)
+ 		{
+ 			case BLOOM_HASH_PROC:
+ 				ok = check_amproc_signature(procform->amproc, INT4OID, false,
+ 											1, 1, opckeytype);
+ 				break;
+ 			default:
+ 				ereport(INFO,
+ 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 						 errmsg("bloom opfamily %s contains function %s with invalid support number %d",
+ 								opfamilyname,
+ 								format_procedure(procform->amproc),
+ 								procform->amprocnum)));
+ 				result = false;
+ 				continue;		/* don't want additional message */
+ 		}
+ 
+ 		if (!ok)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("gist opfamily %s contains function %s with wrong signature for support number %d",
+ 							opfamilyname,
+ 							format_procedure(procform->amproc),
+ 							procform->amprocnum)));
+ 			result = false;
+ 		}
+ 	}
+ 
+ 	/* Check individual operators */
+ 	for (i = 0; i < oprlist->n_members; i++)
+ 	{
+ 		HeapTuple	oprtup = &oprlist->members[i]->tuple;
+ 		Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
+ 
+ 		/* Check it's allowed strategy for bloom */
+ 		if (oprform->amopstrategy < 1 || 
+ 			oprform->amopstrategy > BLOOM_NSTRATEGIES)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains operator %s with invalid strategy number %d",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr),
+ 							oprform->amopstrategy)));
+ 			result = false;
+ 		}
+ 
+ 		/* bloom doesn't support ORDER BY operators */
+ 		if (oprform->amoppurpose != AMOP_SEARCH ||
+ 			OidIsValid(oprform->amopsortfamily))
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains invalid ORDER BY specification for operator %s",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr))));
+ 			result = false;
+ 		}
+ 
+ 		/* Check operator signature --- same for all bloom strategies */
+ 		if (!check_amop_signature(oprform->amopopr, BOOLOID,
+ 								  oprform->amoplefttype,
+ 								  oprform->amoprighttype))
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains operator %s with wrong signature",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr))));
+ 			result = false;
+ 		}
+ 	}
+ 
+ 	/* Now check for inconsistent groups of operators/functions */
+ 	grouplist = identify_opfamily_groups(oprlist, proclist);
+ 	opclassgroup = NULL;
+ 	foreach(lc, grouplist)
+ 	{
+ 		OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
+ 
+ 		/* Remember the group exactly matching the test opclass */
+ 		if (thisgroup->lefttype == opcintype &&
+ 			thisgroup->righttype == opcintype)
+ 			opclassgroup = thisgroup;
+ 
+ 		/*
+ 		 * There is not a lot we can do to check the operator sets, since each
+ 		 * bloom opclass is more or less a law unto itself, and some contain
+ 		 * only operators that are binary-compatible with the opclass datatype
+ 		 * (meaning that empty operator sets can be OK).  That case also means
+ 		 * that we shouldn't insist on nonempty function sets except for the
+ 		 * opclass's own group.
+ 		 */
+ 	}
+ 
+ 	/* Check that the originally-named opclass is complete */
+ 	for (i = 1; i <= BLOOM_NPROC; i++)
+ 	{
+ 		if (opclassgroup &&
+ 			(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
+ 			continue;			/* got it */
+ 		ereport(INFO,
+ 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 				 errmsg("bloom opclass %s is missing support function %d",
+ 						opclassname, i)));
+ 		result = false;
+ 	}
+ 
+ 	ReleaseCatCacheList(proclist);
+ 	ReleaseCatCacheList(oprlist);
+ 	ReleaseSysCache(familytup);
+ 	ReleaseSysCache(classtup);
+ 
+ 	return result;
+ }
diff --git a/contrib/bloom/data/data b/contrib/bloom/data/data
new file mode 100644
index ...eacf3e7
*** a/contrib/bloom/data/data
--- b/contrib/bloom/data/data
***************
*** 0 ****
--- 1,10000 ----
+ 739	3
+ 475	9
+ 45	6
+ 433	1
+ 948	8
+ 926	8
+ 397	7
+ 980	4
+ 212	5
+ 522	9
+ 74	8
+ 77	4
+ 378	9
+ 575	3
+ 625	2
+ 407	4
+ 509	9
+ 252	6
+ 487	7
+ 656	4
+ 485	6
+ 275	9
+ 285	3
+ 277	5
+ 804	8
+ 424	9
+ 553	5
+ 245	9
+ 384	8
+ 202	0
+ 43	6
+ 374	6
+ 490	6
+ 105	10
+ 311	8
+ 411	8
+ 343	7
+ 678	6
+ 942	10
+ 126	7
+ 755	6
+ 625	3
+ 52	9
+ 239	4
+ 690	4
+ 445	7
+ 49	8
+ 285	5
+ 445	4
+ 516	8
+ 151	9
+ 553	5
+ 613	2
+ 123	6
+ 187	2
+ 301	9
+ 800	9
+ 250	9
+ 796	5
+ 288	5
+ 930	7
+ 219	10
+ 553	5
+ 518	10
+ 893	0
+ 754	0
+ 960	3
+ 538	6
+ 537	7
+ 127	7
+ 910	4
+ 666	7
+ 354	9
+ 562	1
+ 405	8
+ 635	3
+ 583	9
+ 313	1
+ 358	8
+ 133	3
+ 864	9
+ 296	8
+ 193	8
+ 396	7
+ 495	5
+ 454	4
+ 952	1
+ 115	3
+ 36	7
+ 455	4
+ 527	1
+ 775	1
+ 945	1
+ 246	3
+ 920	4
+ 554	8
+ 267	8
+ 608	5
+ 684	0
+ 190	2
+ 527	6
+ 584	5
+ 764	7
+ 785	8
+ 376	2
+ 240	9
+ 330	0
+ 13	3
+ 103	3
+ 578	0
+ 639	1
+ 807	9
+ 982	4
+ 365	7
+ 418	6
+ 844	9
+ 199	4
+ 425	10
+ 127	2
+ 762	5
+ 450	0
+ 406	8
+ 17	4
+ 55	1
+ 678	6
+ 143	3
+ 764	9
+ 222	7
+ 364	6
+ 411	8
+ 142	3
+ 728	3
+ 684	2
+ 303	8
+ 363	1
+ 314	8
+ 67	7
+ 592	1
+ 139	6
+ 204	8
+ 279	3
+ 133	0
+ 296	4
+ 788	7
+ 942	2
+ 441	1
+ 454	2
+ 424	1
+ 322	7
+ 949	7
+ 793	3
+ 497	9
+ 984	1
+ 944	1
+ 736	1
+ 940	0
+ 494	1
+ 57	8
+ 429	8
+ 449	4
+ 44	9
+ 454	5
+ 60	9
+ 636	4
+ 606	6
+ 67	4
+ 848	6
+ 259	8
+ 654	2
+ 955	4
+ 351	9
+ 405	8
+ 968	5
+ 634	4
+ 308	1
+ 767	4
+ 974	2
+ 850	0
+ 99	5
+ 416	7
+ 71	5
+ 103	9
+ 48	4
+ 750	7
+ 565	7
+ 92	9
+ 599	5
+ 760	6
+ 960	4
+ 964	3
+ 478	7
+ 620	5
+ 953	5
+ 485	1
+ 957	9
+ 756	0
+ 383	9
+ 946	4
+ 222	7
+ 133	8
+ 401	2
+ 702	0
+ 723	5
+ 568	7
+ 857	5
+ 951	3
+ 264	6
+ 786	2
+ 42	3
+ 268	10
+ 172	0
+ 26	6
+ 884	10
+ 986	1
+ 667	1
+ 893	1
+ 344	6
+ 68	1
+ 58	6
+ 750	9
+ 168	7
+ 249	4
+ 273	0
+ 649	3
+ 306	9
+ 314	5
+ 942	3
+ 33	8
+ 311	0
+ 932	10
+ 138	8
+ 47	5
+ 420	1
+ 550	5
+ 751	3
+ 392	9
+ 1	6
+ 351	3
+ 677	10
+ 588	10
+ 917	9
+ 461	9
+ 242	5
+ 685	6
+ 514	6
+ 531	7
+ 442	6
+ 135	9
+ 693	7
+ 341	4
+ 984	7
+ 362	10
+ 375	7
+ 259	1
+ 713	8
+ 35	6
+ 750	5
+ 489	10
+ 991	2
+ 544	5
+ 791	1
+ 156	2
+ 653	3
+ 96	3
+ 976	4
+ 789	10
+ 170	2
+ 946	5
+ 865	2
+ 597	6
+ 53	6
+ 209	8
+ 128	7
+ 794	1
+ 871	3
+ 623	7
+ 413	8
+ 895	1
+ 70	10
+ 411	0
+ 428	2
+ 6	6
+ 352	10
+ 143	2
+ 156	7
+ 795	2
+ 372	0
+ 11	5
+ 701	8
+ 619	6
+ 142	2
+ 233	6
+ 20	1
+ 621	1
+ 118	0
+ 136	5
+ 232	1
+ 145	6
+ 93	3
+ 800	2
+ 28	6
+ 457	4
+ 598	5
+ 900	3
+ 273	5
+ 870	4
+ 760	1
+ 970	8
+ 231	6
+ 871	3
+ 622	0
+ 895	9
+ 148	0
+ 439	2
+ 328	2
+ 489	4
+ 834	9
+ 756	4
+ 415	7
+ 730	7
+ 175	6
+ 102	9
+ 703	1
+ 715	9
+ 662	6
+ 283	3
+ 592	2
+ 139	7
+ 218	6
+ 981	5
+ 816	5
+ 902	7
+ 417	7
+ 82	8
+ 314	8
+ 519	5
+ 413	6
+ 424	1
+ 693	1
+ 50	4
+ 725	3
+ 639	3
+ 511	8
+ 58	7
+ 356	0
+ 275	2
+ 510	2
+ 822	9
+ 835	9
+ 759	1
+ 717	3
+ 639	1
+ 899	1
+ 246	6
+ 202	3
+ 946	9
+ 629	6
+ 245	1
+ 363	3
+ 870	7
+ 342	1
+ 891	9
+ 322	7
+ 778	2
+ 617	5
+ 307	3
+ 814	9
+ 463	7
+ 8	7
+ 304	2
+ 5	2
+ 138	6
+ 835	4
+ 774	2
+ 685	6
+ 916	0
+ 789	8
+ 878	1
+ 519	7
+ 269	1
+ 193	6
+ 470	0
+ 522	9
+ 720	5
+ 643	0
+ 741	6
+ 275	9
+ 282	1
+ 261	1
+ 306	9
+ 701	2
+ 973	5
+ 28	9
+ 602	5
+ 507	9
+ 683	7
+ 448	2
+ 708	10
+ 88	4
+ 501	7
+ 453	2
+ 379	7
+ 120	7
+ 836	4
+ 717	1
+ 327	4
+ 365	3
+ 908	4
+ 151	5
+ 940	7
+ 381	6
+ 359	8
+ 777	1
+ 799	9
+ 495	3
+ 595	9
+ 542	10
+ 675	7
+ 634	5
+ 44	4
+ 654	4
+ 769	0
+ 671	7
+ 411	8
+ 187	4
+ 481	6
+ 974	8
+ 398	8
+ 907	2
+ 615	4
+ 497	2
+ 349	0
+ 183	0
+ 701	8
+ 535	7
+ 169	2
+ 116	9
+ 208	8
+ 615	6
+ 610	8
+ 970	1
+ 371	9
+ 931	8
+ 695	8
+ 966	3
+ 239	5
+ 520	6
+ 502	7
+ 612	2
+ 520	1
+ 948	7
+ 337	1
+ 627	5
+ 852	2
+ 165	5
+ 45	1
+ 554	4
+ 79	5
+ 185	8
+ 323	2
+ 84	6
+ 613	6
+ 151	1
+ 306	8
+ 318	8
+ 911	3
+ 516	2
+ 331	1
+ 793	2
+ 385	10
+ 646	4
+ 92	2
+ 846	2
+ 686	0
+ 945	0
+ 181	0
+ 572	8
+ 633	7
+ 909	9
+ 486	2
+ 766	4
+ 493	3
+ 645	8
+ 424	4
+ 8	8
+ 395	7
+ 240	5
+ 855	1
+ 659	5
+ 117	6
+ 551	3
+ 634	1
+ 93	3
+ 846	0
+ 206	3
+ 228	10
+ 730	7
+ 253	4
+ 546	7
+ 813	6
+ 487	2
+ 209	7
+ 696	1
+ 814	4
+ 605	9
+ 959	2
+ 230	6
+ 278	3
+ 860	1
+ 324	1
+ 457	6
+ 37	2
+ 273	3
+ 561	8
+ 968	4
+ 373	5
+ 582	6
+ 183	3
+ 646	10
+ 633	3
+ 928	6
+ 407	2
+ 185	7
+ 480	0
+ 810	8
+ 111	3
+ 355	1
+ 453	6
+ 439	0
+ 447	4
+ 388	8
+ 862	10
+ 402	0
+ 247	0
+ 42	9
+ 299	10
+ 472	7
+ 127	7
+ 392	6
+ 702	2
+ 410	8
+ 468	8
+ 960	9
+ 394	4
+ 935	8
+ 806	3
+ 661	7
+ 292	1
+ 714	5
+ 111	8
+ 419	4
+ 725	9
+ 117	9
+ 548	5
+ 460	2
+ 711	9
+ 62	2
+ 636	0
+ 99	0
+ 421	0
+ 870	2
+ 357	5
+ 896	6
+ 594	6
+ 189	7
+ 365	6
+ 116	1
+ 499	2
+ 943	0
+ 742	4
+ 297	5
+ 273	4
+ 631	9
+ 382	7
+ 938	8
+ 765	8
+ 30	1
+ 338	9
+ 771	9
+ 535	10
+ 637	9
+ 568	8
+ 990	1
+ 986	9
+ 114	7
+ 335	4
+ 181	6
+ 770	8
+ 517	2
+ 543	5
+ 954	3
+ 262	10
+ 430	6
+ 910	2
+ 531	4
+ 160	2
+ 345	7
+ 921	3
+ 795	9
+ 267	9
+ 635	6
+ 319	8
+ 211	1
+ 628	7
+ 240	2
+ 182	2
+ 479	4
+ 179	9
+ 43	1
+ 110	6
+ 535	3
+ 743	9
+ 999	7
+ 214	8
+ 571	5
+ 702	2
+ 85	0
+ 22	3
+ 111	6
+ 23	4
+ 821	2
+ 546	3
+ 648	7
+ 210	7
+ 814	3
+ 267	3
+ 590	0
+ 228	6
+ 674	4
+ 382	2
+ 924	1
+ 450	0
+ 106	5
+ 304	2
+ 121	3
+ 568	9
+ 532	1
+ 243	2
+ 839	5
+ 872	7
+ 772	1
+ 2	4
+ 148	2
+ 950	8
+ 673	3
+ 66	6
+ 416	5
+ 606	5
+ 987	9
+ 739	1
+ 237	3
+ 51	8
+ 422	3
+ 949	3
+ 746	8
+ 914	5
+ 959	9
+ 880	1
+ 146	8
+ 929	8
+ 163	10
+ 416	6
+ 511	0
+ 102	5
+ 932	8
+ 606	2
+ 149	7
+ 939	6
+ 951	9
+ 831	7
+ 710	7
+ 215	7
+ 662	1
+ 777	8
+ 926	7
+ 627	1
+ 701	0
+ 668	2
+ 66	8
+ 709	10
+ 611	3
+ 168	8
+ 973	1
+ 330	9
+ 996	2
+ 620	7
+ 907	8
+ 375	6
+ 931	2
+ 377	9
+ 857	0
+ 945	6
+ 49	6
+ 769	1
+ 384	5
+ 113	10
+ 794	3
+ 756	8
+ 389	1
+ 690	4
+ 248	3
+ 90	2
+ 146	5
+ 724	1
+ 616	1
+ 933	5
+ 106	9
+ 31	2
+ 492	8
+ 270	9
+ 279	4
+ 872	1
+ 664	6
+ 840	1
+ 713	5
+ 438	10
+ 841	5
+ 116	10
+ 994	8
+ 63	6
+ 942	10
+ 83	0
+ 874	1
+ 203	4
+ 914	5
+ 242	2
+ 856	1
+ 265	5
+ 742	1
+ 573	5
+ 635	0
+ 416	5
+ 540	5
+ 462	5
+ 373	5
+ 143	3
+ 520	2
+ 363	4
+ 340	6
+ 760	3
+ 40	0
+ 446	9
+ 117	7
+ 416	9
+ 816	10
+ 313	5
+ 0	7
+ 927	5
+ 261	4
+ 74	6
+ 914	2
+ 949	4
+ 443	3
+ 829	8
+ 879	6
+ 37	9
+ 591	5
+ 814	7
+ 195	2
+ 566	0
+ 218	9
+ 462	2
+ 608	4
+ 759	9
+ 778	8
+ 504	7
+ 49	5
+ 127	5
+ 765	10
+ 276	6
+ 544	3
+ 562	1
+ 797	4
+ 843	10
+ 605	4
+ 2	8
+ 288	5
+ 42	9
+ 853	8
+ 765	6
+ 633	3
+ 324	7
+ 722	5
+ 175	5
+ 406	5
+ 130	9
+ 765	7
+ 85	6
+ 68	9
+ 553	7
+ 337	6
+ 497	6
+ 19	5
+ 520	9
+ 340	3
+ 504	10
+ 554	8
+ 656	3
+ 279	8
+ 763	7
+ 283	9
+ 634	0
+ 585	7
+ 609	7
+ 647	2
+ 326	10
+ 717	8
+ 608	7
+ 362	1
+ 608	7
+ 413	1
+ 676	10
+ 940	3
+ 244	2
+ 163	0
+ 903	4
+ 899	5
+ 494	5
+ 256	1
+ 136	9
+ 264	5
+ 886	10
+ 285	5
+ 717	6
+ 621	3
+ 349	0
+ 436	0
+ 2	4
+ 356	2
+ 595	5
+ 251	5
+ 965	2
+ 34	5
+ 633	3
+ 562	8
+ 192	8
+ 231	1
+ 807	5
+ 571	5
+ 163	2
+ 848	5
+ 226	3
+ 536	2
+ 661	9
+ 473	3
+ 412	7
+ 753	4
+ 874	8
+ 837	5
+ 77	4
+ 277	3
+ 225	5
+ 347	0
+ 24	9
+ 555	2
+ 109	4
+ 699	3
+ 688	2
+ 563	3
+ 128	0
+ 604	5
+ 759	4
+ 919	6
+ 143	8
+ 141	2
+ 154	4
+ 488	4
+ 926	8
+ 410	10
+ 752	10
+ 137	9
+ 369	8
+ 197	1
+ 72	8
+ 405	2
+ 795	0
+ 741	6
+ 365	7
+ 187	5
+ 415	3
+ 728	6
+ 745	2
+ 948	7
+ 51	4
+ 621	8
+ 324	8
+ 665	7
+ 595	9
+ 750	7
+ 622	2
+ 867	4
+ 164	6
+ 971	5
+ 267	2
+ 38	7
+ 485	8
+ 251	2
+ 982	2
+ 902	0
+ 556	5
+ 836	9
+ 282	5
+ 573	9
+ 364	3
+ 543	10
+ 477	4
+ 403	6
+ 18	4
+ 171	3
+ 531	2
+ 966	0
+ 974	2
+ 247	10
+ 415	1
+ 988	10
+ 672	8
+ 851	10
+ 325	4
+ 830	7
+ 746	4
+ 675	2
+ 784	1
+ 865	8
+ 450	0
+ 86	10
+ 244	1
+ 998	2
+ 269	2
+ 173	7
+ 394	2
+ 655	1
+ 986	5
+ 20	3
+ 930	8
+ 0	7
+ 224	7
+ 900	0
+ 752	8
+ 809	2
+ 800	9
+ 184	0
+ 947	2
+ 261	2
+ 427	4
+ 899	8
+ 596	6
+ 887	6
+ 60	9
+ 894	10
+ 757	9
+ 667	10
+ 569	6
+ 987	3
+ 331	8
+ 524	1
+ 691	7
+ 174	6
+ 891	4
+ 854	3
+ 870	8
+ 139	5
+ 307	0
+ 48	4
+ 933	9
+ 358	7
+ 836	0
+ 670	4
+ 591	7
+ 726	9
+ 454	3
+ 53	1
+ 959	2
+ 783	8
+ 663	6
+ 168	5
+ 389	3
+ 999	7
+ 334	0
+ 64	3
+ 989	4
+ 957	8
+ 447	6
+ 231	0
+ 285	10
+ 960	7
+ 208	0
+ 883	2
+ 240	7
+ 16	9
+ 302	2
+ 435	7
+ 490	4
+ 388	8
+ 481	5
+ 91	5
+ 874	0
+ 296	3
+ 675	5
+ 359	10
+ 484	3
+ 698	7
+ 332	6
+ 858	6
+ 247	9
+ 475	5
+ 57	9
+ 241	5
+ 344	6
+ 371	8
+ 81	5
+ 296	10
+ 509	6
+ 277	2
+ 120	6
+ 143	6
+ 955	8
+ 296	3
+ 421	2
+ 860	7
+ 28	3
+ 217	1
+ 244	5
+ 632	6
+ 87	0
+ 414	2
+ 465	7
+ 123	10
+ 303	4
+ 158	4
+ 36	3
+ 27	10
+ 142	3
+ 278	6
+ 476	1
+ 231	5
+ 472	4
+ 588	7
+ 907	2
+ 305	10
+ 223	7
+ 161	7
+ 428	3
+ 662	7
+ 684	8
+ 154	7
+ 121	2
+ 711	3
+ 503	10
+ 826	10
+ 127	1
+ 483	6
+ 506	1
+ 316	4
+ 292	6
+ 407	5
+ 339	6
+ 203	8
+ 853	9
+ 499	5
+ 684	7
+ 257	8
+ 833	10
+ 68	3
+ 958	9
+ 316	1
+ 950	8
+ 685	5
+ 870	0
+ 869	2
+ 622	3
+ 676	10
+ 844	9
+ 729	7
+ 743	2
+ 234	4
+ 881	5
+ 233	7
+ 460	3
+ 51	4
+ 193	4
+ 503	1
+ 165	2
+ 600	0
+ 189	5
+ 197	8
+ 745	9
+ 773	6
+ 752	5
+ 285	5
+ 730	5
+ 923	6
+ 10	2
+ 324	5
+ 455	4
+ 888	6
+ 741	4
+ 792	9
+ 579	4
+ 942	8
+ 862	1
+ 580	6
+ 12	4
+ 196	8
+ 854	5
+ 259	6
+ 1	2
+ 195	0
+ 336	5
+ 481	8
+ 894	4
+ 440	6
+ 760	2
+ 542	3
+ 625	5
+ 107	5
+ 622	7
+ 94	6
+ 40	3
+ 397	9
+ 771	7
+ 479	8
+ 837	7
+ 783	2
+ 192	3
+ 964	1
+ 633	4
+ 721	4
+ 637	3
+ 732	3
+ 746	8
+ 749	4
+ 527	8
+ 2	6
+ 133	4
+ 462	9
+ 54	9
+ 677	9
+ 613	5
+ 64	8
+ 725	0
+ 891	4
+ 433	6
+ 751	1
+ 876	5
+ 332	6
+ 324	1
+ 990	9
+ 925	10
+ 418	1
+ 390	9
+ 962	4
+ 820	6
+ 335	4
+ 99	4
+ 239	8
+ 427	1
+ 182	9
+ 743	9
+ 930	6
+ 418	3
+ 241	7
+ 344	2
+ 593	3
+ 223	0
+ 326	6
+ 891	3
+ 58	7
+ 928	4
+ 145	0
+ 792	4
+ 851	2
+ 514	0
+ 80	3
+ 967	0
+ 875	4
+ 272	1
+ 126	6
+ 347	7
+ 884	6
+ 730	2
+ 184	6
+ 498	2
+ 333	4
+ 635	5
+ 453	4
+ 861	3
+ 647	4
+ 338	7
+ 632	3
+ 736	5
+ 689	0
+ 624	8
+ 623	10
+ 534	5
+ 541	3
+ 717	7
+ 885	2
+ 967	2
+ 641	6
+ 696	1
+ 29	6
+ 399	7
+ 933	7
+ 403	6
+ 41	1
+ 73	7
+ 147	7
+ 546	8
+ 668	1
+ 278	2
+ 344	10
+ 934	2
+ 209	9
+ 447	8
+ 503	1
+ 944	5
+ 700	3
+ 208	6
+ 79	6
+ 198	1
+ 750	3
+ 851	9
+ 967	4
+ 668	6
+ 477	9
+ 843	8
+ 940	8
+ 51	1
+ 678	5
+ 999	2
+ 641	9
+ 713	3
+ 285	9
+ 974	4
+ 532	2
+ 485	3
+ 442	3
+ 179	4
+ 733	8
+ 44	2
+ 792	9
+ 32	7
+ 664	1
+ 880	3
+ 581	9
+ 523	2
+ 822	2
+ 563	1
+ 157	5
+ 471	7
+ 709	10
+ 971	2
+ 292	2
+ 561	0
+ 997	6
+ 236	8
+ 491	3
+ 521	2
+ 351	4
+ 498	9
+ 281	0
+ 153	1
+ 258	7
+ 209	4
+ 253	7
+ 105	10
+ 636	1
+ 113	9
+ 227	7
+ 954	2
+ 278	2
+ 14	8
+ 459	5
+ 926	8
+ 937	4
+ 742	2
+ 446	9
+ 320	7
+ 611	5
+ 120	9
+ 210	2
+ 827	8
+ 301	9
+ 775	5
+ 614	7
+ 753	9
+ 918	8
+ 663	4
+ 302	6
+ 187	2
+ 13	9
+ 457	5
+ 824	8
+ 163	4
+ 307	3
+ 300	5
+ 508	1
+ 363	8
+ 67	1
+ 338	7
+ 866	1
+ 573	8
+ 858	2
+ 161	2
+ 824	3
+ 399	8
+ 277	9
+ 295	1
+ 633	5
+ 536	9
+ 742	8
+ 456	3
+ 963	8
+ 61	0
+ 956	4
+ 710	8
+ 490	3
+ 606	3
+ 519	8
+ 508	3
+ 116	9
+ 179	4
+ 762	5
+ 494	4
+ 934	0
+ 335	7
+ 867	8
+ 926	8
+ 610	10
+ 859	6
+ 386	6
+ 389	9
+ 852	10
+ 224	4
+ 763	7
+ 713	9
+ 638	9
+ 272	4
+ 367	8
+ 796	3
+ 796	1
+ 977	7
+ 921	9
+ 493	5
+ 890	4
+ 98	3
+ 921	5
+ 152	8
+ 482	4
+ 143	2
+ 108	9
+ 124	7
+ 750	4
+ 147	1
+ 162	9
+ 418	10
+ 73	4
+ 622	10
+ 298	1
+ 526	2
+ 466	6
+ 464	4
+ 111	6
+ 159	6
+ 992	3
+ 837	1
+ 159	10
+ 847	9
+ 357	10
+ 26	5
+ 937	4
+ 478	0
+ 839	1
+ 5	1
+ 214	5
+ 325	7
+ 156	8
+ 66	3
+ 405	2
+ 859	4
+ 527	7
+ 498	7
+ 658	3
+ 595	0
+ 339	6
+ 535	3
+ 65	0
+ 286	9
+ 112	3
+ 41	3
+ 823	4
+ 6	10
+ 154	1
+ 245	6
+ 295	1
+ 957	8
+ 800	5
+ 508	5
+ 801	1
+ 473	1
+ 723	0
+ 415	8
+ 21	7
+ 691	1
+ 993	7
+ 460	8
+ 97	5
+ 795	3
+ 536	0
+ 811	8
+ 144	8
+ 654	9
+ 224	2
+ 403	0
+ 263	9
+ 165	10
+ 884	6
+ 774	9
+ 282	5
+ 39	3
+ 197	5
+ 91	3
+ 964	9
+ 546	5
+ 926	4
+ 332	1
+ 127	10
+ 15	4
+ 146	4
+ 376	4
+ 293	5
+ 396	2
+ 120	2
+ 83	4
+ 636	1
+ 677	8
+ 620	8
+ 128	6
+ 655	7
+ 84	6
+ 32	4
+ 651	2
+ 400	7
+ 510	5
+ 83	9
+ 957	4
+ 426	4
+ 554	5
+ 523	6
+ 949	2
+ 758	6
+ 992	4
+ 395	1
+ 962	0
+ 794	0
+ 630	8
+ 461	3
+ 984	9
+ 947	5
+ 408	0
+ 380	4
+ 407	8
+ 717	10
+ 352	2
+ 598	3
+ 399	4
+ 927	4
+ 734	3
+ 510	7
+ 371	3
+ 742	0
+ 129	2
+ 283	1
+ 63	2
+ 608	5
+ 261	10
+ 835	7
+ 793	6
+ 628	1
+ 793	2
+ 446	2
+ 582	4
+ 583	3
+ 695	1
+ 13	1
+ 397	8
+ 68	5
+ 957	4
+ 641	0
+ 582	2
+ 491	8
+ 235	3
+ 510	0
+ 879	1
+ 173	7
+ 365	6
+ 863	9
+ 992	4
+ 264	7
+ 540	3
+ 754	9
+ 32	8
+ 464	10
+ 174	1
+ 9	8
+ 353	5
+ 598	6
+ 827	1
+ 616	7
+ 247	8
+ 377	6
+ 407	2
+ 558	4
+ 686	8
+ 86	2
+ 99	8
+ 163	1
+ 662	6
+ 120	8
+ 731	1
+ 591	1
+ 630	2
+ 671	5
+ 298	3
+ 162	5
+ 75	5
+ 155	5
+ 779	7
+ 880	5
+ 535	10
+ 691	6
+ 806	9
+ 764	5
+ 480	9
+ 303	2
+ 13	9
+ 294	6
+ 84	10
+ 100	4
+ 252	3
+ 926	3
+ 801	1
+ 808	6
+ 794	7
+ 45	3
+ 655	7
+ 963	5
+ 589	7
+ 929	1
+ 611	2
+ 279	6
+ 127	6
+ 267	2
+ 538	4
+ 592	8
+ 629	5
+ 117	4
+ 599	9
+ 10	4
+ 614	1
+ 722	3
+ 790	7
+ 730	4
+ 413	7
+ 447	0
+ 891	7
+ 648	0
+ 299	9
+ 228	8
+ 282	8
+ 627	9
+ 338	7
+ 340	9
+ 669	3
+ 330	3
+ 404	1
+ 552	2
+ 738	3
+ 574	2
+ 941	0
+ 174	8
+ 747	8
+ 849	0
+ 738	1
+ 884	0
+ 897	5
+ 931	2
+ 256	3
+ 173	9
+ 621	5
+ 209	0
+ 556	8
+ 220	3
+ 43	8
+ 444	10
+ 815	6
+ 816	6
+ 441	7
+ 609	2
+ 742	5
+ 199	6
+ 4	1
+ 875	3
+ 400	0
+ 185	0
+ 551	4
+ 46	1
+ 155	3
+ 400	2
+ 60	8
+ 183	9
+ 463	10
+ 436	9
+ 665	0
+ 82	4
+ 538	3
+ 47	5
+ 410	9
+ 802	8
+ 970	10
+ 832	5
+ 381	9
+ 627	5
+ 145	0
+ 734	2
+ 872	9
+ 79	3
+ 916	5
+ 238	6
+ 560	3
+ 988	1
+ 602	0
+ 639	0
+ 956	4
+ 823	9
+ 429	7
+ 446	8
+ 533	1
+ 346	7
+ 101	1
+ 883	10
+ 997	10
+ 307	9
+ 477	5
+ 495	0
+ 865	5
+ 135	5
+ 517	8
+ 479	5
+ 215	3
+ 399	6
+ 957	8
+ 454	5
+ 919	8
+ 168	0
+ 880	1
+ 992	9
+ 13	3
+ 791	5
+ 844	3
+ 527	7
+ 768	7
+ 176	3
+ 435	7
+ 759	7
+ 957	2
+ 295	9
+ 4	7
+ 403	9
+ 548	6
+ 943	4
+ 622	9
+ 305	6
+ 235	1
+ 124	1
+ 381	7
+ 789	1
+ 312	10
+ 434	7
+ 619	2
+ 398	6
+ 351	7
+ 489	4
+ 442	9
+ 279	10
+ 463	2
+ 418	1
+ 158	7
+ 720	4
+ 819	8
+ 473	2
+ 496	3
+ 349	8
+ 226	8
+ 556	8
+ 976	10
+ 421	3
+ 648	9
+ 683	1
+ 803	10
+ 80	3
+ 184	5
+ 352	3
+ 221	1
+ 736	0
+ 917	2
+ 240	4
+ 470	6
+ 221	7
+ 372	8
+ 542	3
+ 731	10
+ 676	4
+ 874	4
+ 469	7
+ 321	5
+ 943	5
+ 46	3
+ 848	3
+ 367	6
+ 307	3
+ 793	5
+ 697	3
+ 135	9
+ 959	5
+ 695	5
+ 855	4
+ 464	5
+ 806	3
+ 890	3
+ 14	2
+ 822	10
+ 715	9
+ 253	6
+ 135	6
+ 147	4
+ 904	9
+ 988	6
+ 203	1
+ 519	2
+ 630	2
+ 663	5
+ 640	1
+ 16	4
+ 465	9
+ 720	5
+ 115	5
+ 437	8
+ 410	7
+ 393	5
+ 309	5
+ 987	2
+ 479	10
+ 814	7
+ 97	3
+ 844	7
+ 547	5
+ 212	2
+ 634	2
+ 634	1
+ 133	4
+ 579	2
+ 896	0
+ 79	3
+ 706	5
+ 852	0
+ 12	8
+ 228	5
+ 813	0
+ 173	9
+ 376	0
+ 637	9
+ 524	8
+ 111	2
+ 76	7
+ 258	2
+ 98	8
+ 457	10
+ 853	5
+ 301	6
+ 8	2
+ 574	0
+ 991	8
+ 511	8
+ 845	7
+ 713	2
+ 702	4
+ 144	2
+ 199	3
+ 385	3
+ 999	6
+ 483	1
+ 481	9
+ 91	3
+ 475	4
+ 893	5
+ 544	5
+ 503	5
+ 270	0
+ 338	1
+ 698	1
+ 336	4
+ 402	5
+ 626	6
+ 735	0
+ 875	7
+ 655	4
+ 830	1
+ 298	9
+ 469	8
+ 313	4
+ 256	9
+ 830	8
+ 392	1
+ 773	7
+ 215	5
+ 782	6
+ 871	2
+ 31	5
+ 784	8
+ 509	7
+ 499	2
+ 17	3
+ 299	3
+ 250	8
+ 89	6
+ 130	3
+ 421	10
+ 104	8
+ 59	9
+ 543	3
+ 348	3
+ 824	2
+ 508	9
+ 717	3
+ 620	2
+ 950	1
+ 390	10
+ 448	7
+ 282	7
+ 457	4
+ 262	6
+ 716	7
+ 546	8
+ 496	6
+ 697	0
+ 879	0
+ 363	7
+ 265	9
+ 557	10
+ 163	2
+ 209	1
+ 296	6
+ 80	7
+ 288	4
+ 442	7
+ 733	7
+ 332	4
+ 387	9
+ 269	9
+ 483	10
+ 921	4
+ 12	3
+ 64	3
+ 155	6
+ 260	3
+ 799	5
+ 431	1
+ 68	5
+ 839	4
+ 873	3
+ 101	6
+ 986	4
+ 55	4
+ 311	3
+ 255	8
+ 290	2
+ 155	3
+ 460	2
+ 579	6
+ 840	8
+ 933	6
+ 308	4
+ 735	4
+ 875	6
+ 733	7
+ 855	8
+ 353	8
+ 268	4
+ 213	6
+ 732	5
+ 372	0
+ 644	5
+ 324	1
+ 746	9
+ 718	6
+ 743	7
+ 225	1
+ 15	10
+ 428	9
+ 534	2
+ 637	4
+ 996	10
+ 230	3
+ 399	4
+ 842	1
+ 911	2
+ 153	6
+ 741	5
+ 658	5
+ 380	4
+ 72	1
+ 28	3
+ 174	0
+ 258	6
+ 933	8
+ 763	6
+ 181	8
+ 561	4
+ 22	10
+ 854	9
+ 90	8
+ 78	2
+ 320	8
+ 719	10
+ 305	1
+ 354	4
+ 222	4
+ 675	4
+ 425	9
+ 997	4
+ 725	8
+ 928	9
+ 518	5
+ 317	5
+ 447	2
+ 405	5
+ 936	5
+ 780	3
+ 302	5
+ 233	6
+ 598	6
+ 985	8
+ 969	7
+ 215	4
+ 594	2
+ 752	3
+ 973	7
+ 224	5
+ 167	5
+ 32	6
+ 712	4
+ 152	6
+ 920	9
+ 903	2
+ 430	1
+ 830	0
+ 724	8
+ 848	7
+ 477	1
+ 88	1
+ 276	8
+ 389	2
+ 519	6
+ 740	7
+ 154	8
+ 301	9
+ 209	5
+ 514	1
+ 385	4
+ 351	8
+ 553	2
+ 843	3
+ 998	7
+ 971	5
+ 754	1
+ 545	0
+ 898	9
+ 279	4
+ 547	0
+ 104	7
+ 791	4
+ 568	10
+ 858	1
+ 129	2
+ 499	5
+ 58	1
+ 662	9
+ 330	7
+ 592	3
+ 134	3
+ 359	7
+ 376	3
+ 613	7
+ 675	2
+ 674	8
+ 862	5
+ 183	4
+ 465	0
+ 512	6
+ 284	0
+ 74	3
+ 63	7
+ 244	4
+ 395	8
+ 693	5
+ 181	1
+ 208	6
+ 309	8
+ 212	10
+ 981	9
+ 763	8
+ 352	9
+ 273	8
+ 988	8
+ 410	3
+ 796	5
+ 614	9
+ 220	9
+ 251	6
+ 693	9
+ 144	9
+ 995	4
+ 432	3
+ 174	6
+ 289	2
+ 531	1
+ 998	9
+ 997	3
+ 700	10
+ 57	1
+ 257	9
+ 594	9
+ 711	8
+ 730	10
+ 429	4
+ 905	6
+ 298	9
+ 926	7
+ 205	1
+ 374	5
+ 254	9
+ 545	3
+ 788	5
+ 524	5
+ 528	6
+ 598	8
+ 433	2
+ 656	1
+ 6	4
+ 105	4
+ 809	0
+ 8	1
+ 910	9
+ 836	1
+ 34	2
+ 608	3
+ 115	2
+ 541	9
+ 696	1
+ 391	2
+ 645	10
+ 8	1
+ 180	7
+ 220	2
+ 51	3
+ 621	9
+ 335	6
+ 966	2
+ 564	8
+ 359	6
+ 12	10
+ 887	1
+ 120	4
+ 31	8
+ 492	4
+ 40	1
+ 410	0
+ 213	6
+ 713	4
+ 777	8
+ 759	4
+ 623	1
+ 28	6
+ 338	6
+ 390	7
+ 191	4
+ 663	1
+ 530	8
+ 505	6
+ 599	10
+ 983	6
+ 133	4
+ 687	3
+ 984	4
+ 780	8
+ 163	5
+ 160	8
+ 632	2
+ 374	10
+ 780	8
+ 666	10
+ 167	3
+ 48	7
+ 112	6
+ 258	7
+ 549	2
+ 350	7
+ 635	0
+ 27	6
+ 437	8
+ 380	6
+ 345	5
+ 386	10
+ 727	8
+ 947	5
+ 525	6
+ 477	7
+ 942	5
+ 389	1
+ 77	6
+ 765	6
+ 889	1
+ 308	5
+ 153	3
+ 142	6
+ 143	5
+ 191	5
+ 62	6
+ 465	8
+ 338	4
+ 296	9
+ 25	8
+ 555	10
+ 298	9
+ 20	4
+ 591	8
+ 2	5
+ 901	3
+ 3	1
+ 645	1
+ 645	8
+ 667	8
+ 276	7
+ 413	7
+ 517	8
+ 153	8
+ 613	2
+ 586	2
+ 144	9
+ 112	2
+ 259	7
+ 949	3
+ 183	9
+ 570	2
+ 904	2
+ 331	5
+ 3	10
+ 385	3
+ 726	8
+ 20	2
+ 549	2
+ 56	2
+ 351	6
+ 330	5
+ 525	4
+ 658	8
+ 144	6
+ 45	3
+ 458	6
+ 513	4
+ 830	8
+ 911	8
+ 841	3
+ 112	6
+ 94	1
+ 810	6
+ 305	9
+ 806	7
+ 508	1
+ 150	0
+ 577	8
+ 817	7
+ 416	9
+ 49	9
+ 477	6
+ 236	3
+ 405	1
+ 140	2
+ 443	3
+ 812	5
+ 385	6
+ 181	7
+ 489	10
+ 345	10
+ 122	5
+ 30	7
+ 304	8
+ 421	7
+ 710	5
+ 594	2
+ 31	8
+ 493	4
+ 977	6
+ 681	4
+ 886	5
+ 958	3
+ 116	1
+ 960	6
+ 126	3
+ 602	2
+ 801	6
+ 948	1
+ 480	4
+ 825	2
+ 840	4
+ 376	9
+ 249	9
+ 307	2
+ 504	10
+ 646	4
+ 482	6
+ 661	6
+ 744	6
+ 203	9
+ 927	8
+ 118	7
+ 438	1
+ 833	9
+ 436	7
+ 108	3
+ 77	5
+ 147	3
+ 354	5
+ 552	9
+ 443	2
+ 248	9
+ 802	9
+ 523	5
+ 530	7
+ 416	5
+ 532	5
+ 186	10
+ 600	0
+ 887	0
+ 677	10
+ 312	8
+ 479	5
+ 80	8
+ 913	6
+ 691	4
+ 830	9
+ 281	6
+ 848	8
+ 178	4
+ 530	6
+ 835	1
+ 128	0
+ 31	7
+ 39	9
+ 765	7
+ 914	1
+ 470	4
+ 537	6
+ 226	4
+ 183	9
+ 806	0
+ 855	1
+ 645	7
+ 890	8
+ 81	4
+ 418	9
+ 482	5
+ 937	5
+ 274	10
+ 432	0
+ 692	3
+ 116	2
+ 738	7
+ 713	10
+ 102	9
+ 881	9
+ 909	7
+ 994	6
+ 439	9
+ 378	5
+ 304	8
+ 436	8
+ 341	4
+ 299	6
+ 349	7
+ 653	0
+ 76	8
+ 203	8
+ 421	9
+ 778	5
+ 812	7
+ 431	7
+ 395	4
+ 275	8
+ 309	7
+ 354	6
+ 449	8
+ 398	8
+ 163	7
+ 405	5
+ 428	1
+ 552	5
+ 828	8
+ 319	2
+ 672	1
+ 772	5
+ 756	2
+ 205	2
+ 628	5
+ 986	9
+ 134	3
+ 550	6
+ 130	9
+ 373	3
+ 644	8
+ 805	1
+ 837	4
+ 576	7
+ 113	9
+ 913	8
+ 992	7
+ 270	7
+ 889	5
+ 899	5
+ 956	9
+ 455	1
+ 225	0
+ 673	4
+ 952	0
+ 648	6
+ 825	5
+ 669	7
+ 811	2
+ 326	9
+ 140	2
+ 710	1
+ 925	10
+ 881	8
+ 454	8
+ 331	4
+ 665	8
+ 500	9
+ 791	2
+ 245	7
+ 219	9
+ 339	0
+ 347	0
+ 705	2
+ 253	0
+ 82	4
+ 270	8
+ 526	2
+ 771	4
+ 8	2
+ 186	3
+ 635	9
+ 126	1
+ 741	9
+ 307	10
+ 659	5
+ 878	10
+ 570	2
+ 5	3
+ 383	3
+ 306	5
+ 651	6
+ 256	2
+ 769	0
+ 583	8
+ 251	8
+ 117	9
+ 620	2
+ 21	4
+ 158	3
+ 346	8
+ 854	2
+ 814	4
+ 449	8
+ 699	8
+ 78	0
+ 296	7
+ 580	6
+ 905	3
+ 578	5
+ 127	8
+ 257	2
+ 715	9
+ 486	7
+ 237	6
+ 64	6
+ 461	9
+ 808	3
+ 342	3
+ 95	0
+ 89	2
+ 47	4
+ 901	6
+ 937	8
+ 977	5
+ 294	1
+ 344	6
+ 348	1
+ 428	8
+ 795	7
+ 478	9
+ 249	9
+ 777	1
+ 215	1
+ 314	3
+ 161	4
+ 482	2
+ 787	4
+ 835	7
+ 190	8
+ 238	5
+ 917	6
+ 36	3
+ 641	5
+ 100	4
+ 130	6
+ 295	4
+ 517	1
+ 436	7
+ 191	7
+ 42	4
+ 152	5
+ 559	9
+ 908	4
+ 663	1
+ 207	9
+ 583	1
+ 483	6
+ 390	1
+ 84	5
+ 561	2
+ 67	9
+ 593	6
+ 928	0
+ 316	1
+ 780	4
+ 470	9
+ 882	0
+ 871	8
+ 424	5
+ 888	6
+ 434	5
+ 756	9
+ 90	1
+ 42	2
+ 636	6
+ 387	7
+ 459	10
+ 288	4
+ 11	6
+ 505	8
+ 962	10
+ 722	8
+ 4	6
+ 634	4
+ 125	5
+ 59	6
+ 994	8
+ 476	1
+ 962	5
+ 257	6
+ 121	6
+ 301	6
+ 625	6
+ 966	6
+ 193	5
+ 426	2
+ 445	1
+ 1000	4
+ 739	6
+ 876	9
+ 157	9
+ 424	2
+ 751	9
+ 234	7
+ 418	5
+ 310	5
+ 135	6
+ 118	8
+ 200	1
+ 396	4
+ 555	8
+ 548	0
+ 969	5
+ 449	7
+ 183	3
+ 572	3
+ 261	10
+ 490	0
+ 896	7
+ 724	3
+ 214	0
+ 853	3
+ 645	10
+ 109	8
+ 56	5
+ 237	6
+ 326	8
+ 611	3
+ 334	1
+ 3	5
+ 385	6
+ 856	6
+ 571	3
+ 658	5
+ 69	4
+ 782	3
+ 415	6
+ 633	1
+ 607	7
+ 904	7
+ 248	1
+ 274	6
+ 927	9
+ 869	3
+ 945	9
+ 777	3
+ 447	6
+ 977	0
+ 978	6
+ 485	0
+ 16	3
+ 331	4
+ 902	10
+ 491	5
+ 707	4
+ 172	10
+ 537	4
+ 528	5
+ 331	4
+ 724	3
+ 268	5
+ 607	7
+ 134	6
+ 733	1
+ 219	2
+ 159	2
+ 485	5
+ 666	4
+ 455	2
+ 897	2
+ 552	1
+ 116	1
+ 515	6
+ 552	8
+ 42	3
+ 123	3
+ 777	7
+ 25	9
+ 314	8
+ 23	5
+ 975	2
+ 767	5
+ 673	4
+ 849	1
+ 591	7
+ 290	1
+ 815	4
+ 232	3
+ 51	8
+ 176	1
+ 61	3
+ 403	8
+ 28	4
+ 748	3
+ 185	8
+ 875	2
+ 953	6
+ 621	6
+ 76	5
+ 754	7
+ 216	0
+ 810	0
+ 451	0
+ 360	5
+ 826	5
+ 596	9
+ 834	10
+ 724	9
+ 426	5
+ 205	6
+ 244	1
+ 771	2
+ 724	4
+ 823	8
+ 863	6
+ 466	1
+ 622	3
+ 109	1
+ 318	5
+ 576	1
+ 6	2
+ 30	8
+ 170	8
+ 702	6
+ 226	9
+ 207	5
+ 989	10
+ 667	7
+ 372	5
+ 512	2
+ 67	10
+ 313	7
+ 254	4
+ 762	6
+ 892	3
+ 715	9
+ 510	7
+ 738	7
+ 498	4
+ 276	7
+ 348	5
+ 194	3
+ 462	9
+ 49	8
+ 350	6
+ 68	4
+ 539	4
+ 106	8
+ 804	9
+ 365	7
+ 207	1
+ 595	7
+ 824	3
+ 397	3
+ 773	7
+ 47	1
+ 156	2
+ 457	6
+ 101	5
+ 452	5
+ 66	5
+ 869	6
+ 902	10
+ 397	7
+ 844	8
+ 403	1
+ 841	10
+ 768	7
+ 330	2
+ 988	1
+ 837	0
+ 223	10
+ 276	7
+ 611	4
+ 185	1
+ 829	3
+ 583	7
+ 855	5
+ 672	3
+ 190	5
+ 14	6
+ 567	9
+ 590	3
+ 521	9
+ 498	5
+ 22	3
+ 544	2
+ 328	8
+ 925	9
+ 197	1
+ 1	0
+ 361	6
+ 723	2
+ 68	4
+ 469	3
+ 911	5
+ 851	5
+ 338	4
+ 812	9
+ 361	3
+ 368	4
+ 645	9
+ 629	10
+ 732	6
+ 911	9
+ 663	9
+ 955	0
+ 495	7
+ 241	6
+ 74	7
+ 820	10
+ 192	7
+ 462	5
+ 112	3
+ 388	5
+ 584	8
+ 856	2
+ 667	5
+ 201	4
+ 38	1
+ 329	7
+ 24	3
+ 726	5
+ 963	10
+ 81	0
+ 676	9
+ 21	9
+ 573	5
+ 398	7
+ 757	8
+ 157	3
+ 542	0
+ 569	2
+ 498	8
+ 608	5
+ 882	9
+ 238	9
+ 221	10
+ 424	2
+ 931	5
+ 221	6
+ 407	2
+ 476	10
+ 725	9
+ 664	5
+ 660	8
+ 822	2
+ 835	4
+ 411	3
+ 160	0
+ 870	0
+ 956	1
+ 947	2
+ 73	4
+ 362	0
+ 877	6
+ 612	3
+ 824	1
+ 265	5
+ 963	9
+ 31	6
+ 751	9
+ 825	6
+ 243	2
+ 920	4
+ 256	8
+ 445	2
+ 898	4
+ 390	10
+ 764	8
+ 975	6
+ 335	6
+ 926	2
+ 675	2
+ 708	6
+ 121	7
+ 261	9
+ 592	1
+ 458	8
+ 323	4
+ 238	6
+ 167	7
+ 791	1
+ 75	2
+ 36	8
+ 933	0
+ 481	3
+ 597	4
+ 427	3
+ 598	1
+ 910	7
+ 874	2
+ 590	5
+ 258	0
+ 300	6
+ 425	5
+ 160	6
+ 221	10
+ 657	3
+ 131	7
+ 134	1
+ 703	6
+ 332	3
+ 22	8
+ 573	6
+ 894	5
+ 339	8
+ 655	9
+ 234	9
+ 978	5
+ 494	4
+ 73	7
+ 995	3
+ 603	7
+ 588	7
+ 345	7
+ 799	0
+ 338	1
+ 349	4
+ 889	9
+ 980	8
+ 404	3
+ 551	1
+ 249	8
+ 972	2
+ 319	5
+ 629	4
+ 118	6
+ 685	7
+ 277	3
+ 456	6
+ 996	3
+ 670	3
+ 385	0
+ 694	3
+ 940	7
+ 57	3
+ 993	6
+ 404	2
+ 392	4
+ 468	7
+ 840	1
+ 103	10
+ 721	8
+ 680	10
+ 61	1
+ 620	1
+ 392	3
+ 391	8
+ 310	1
+ 51	3
+ 759	1
+ 595	8
+ 716	10
+ 993	1
+ 374	5
+ 819	2
+ 558	9
+ 172	3
+ 710	9
+ 278	8
+ 989	9
+ 829	4
+ 188	2
+ 158	5
+ 305	2
+ 748	1
+ 317	3
+ 815	0
+ 341	8
+ 141	7
+ 270	10
+ 929	8
+ 883	1
+ 108	6
+ 954	4
+ 364	9
+ 283	2
+ 324	5
+ 413	5
+ 970	7
+ 691	7
+ 781	0
+ 61	6
+ 41	4
+ 405	2
+ 118	7
+ 142	0
+ 504	0
+ 148	6
+ 618	1
+ 997	10
+ 46	3
+ 175	4
+ 752	6
+ 852	7
+ 306	5
+ 440	1
+ 552	5
+ 684	6
+ 904	1
+ 775	0
+ 764	9
+ 68	3
+ 942	2
+ 880	6
+ 319	9
+ 542	4
+ 157	7
+ 734	9
+ 306	6
+ 632	6
+ 130	1
+ 699	7
+ 574	4
+ 275	5
+ 472	1
+ 500	2
+ 968	6
+ 505	9
+ 785	4
+ 470	1
+ 261	0
+ 468	4
+ 730	2
+ 328	0
+ 788	10
+ 648	9
+ 32	3
+ 601	6
+ 730	9
+ 83	2
+ 926	6
+ 439	9
+ 151	9
+ 803	9
+ 327	3
+ 39	6
+ 285	5
+ 7	0
+ 709	3
+ 52	5
+ 294	7
+ 416	3
+ 47	0
+ 931	8
+ 892	0
+ 979	8
+ 597	4
+ 712	7
+ 361	5
+ 685	7
+ 789	7
+ 277	1
+ 231	3
+ 89	9
+ 618	1
+ 437	9
+ 840	9
+ 237	9
+ 869	2
+ 664	8
+ 181	6
+ 580	8
+ 61	3
+ 527	4
+ 808	2
+ 110	6
+ 936	4
+ 671	2
+ 670	8
+ 106	3
+ 901	5
+ 199	7
+ 395	4
+ 629	3
+ 603	3
+ 26	8
+ 936	6
+ 562	10
+ 898	1
+ 418	7
+ 301	5
+ 303	2
+ 915	10
+ 403	6
+ 733	5
+ 873	6
+ 52	1
+ 376	4
+ 508	0
+ 712	1
+ 297	7
+ 894	2
+ 344	5
+ 229	2
+ 546	6
+ 948	8
+ 176	3
+ 84	1
+ 225	5
+ 677	10
+ 996	5
+ 592	0
+ 622	10
+ 495	1
+ 972	2
+ 240	3
+ 944	1
+ 502	3
+ 591	7
+ 530	1
+ 379	5
+ 984	6
+ 730	1
+ 646	10
+ 555	3
+ 912	6
+ 873	5
+ 598	5
+ 472	1
+ 625	4
+ 299	9
+ 713	2
+ 1000	2
+ 531	6
+ 946	1
+ 728	3
+ 540	7
+ 881	3
+ 781	5
+ 223	3
+ 850	1
+ 885	7
+ 639	5
+ 218	1
+ 576	8
+ 555	9
+ 707	3
+ 120	7
+ 483	7
+ 298	4
+ 712	0
+ 755	3
+ 739	6
+ 521	5
+ 162	7
+ 854	0
+ 880	7
+ 735	5
+ 223	10
+ 630	8
+ 795	2
+ 676	5
+ 454	8
+ 208	9
+ 446	5
+ 367	2
+ 532	1
+ 410	3
+ 757	9
+ 789	9
+ 676	6
+ 931	6
+ 384	7
+ 74	6
+ 618	7
+ 407	4
+ 890	1
+ 915	3
+ 878	1
+ 281	3
+ 629	6
+ 482	2
+ 769	9
+ 431	5
+ 824	2
+ 445	5
+ 865	4
+ 55	2
+ 42	1
+ 855	7
+ 834	3
+ 73	7
+ 344	10
+ 68	2
+ 111	3
+ 545	7
+ 996	0
+ 901	8
+ 920	3
+ 291	7
+ 553	7
+ 243	4
+ 111	3
+ 666	2
+ 427	5
+ 812	3
+ 783	9
+ 985	1
+ 873	1
+ 348	10
+ 401	9
+ 724	4
+ 921	6
+ 163	8
+ 957	5
+ 584	5
+ 189	8
+ 928	3
+ 125	6
+ 452	6
+ 114	3
+ 814	9
+ 150	8
+ 23	0
+ 851	4
+ 7	3
+ 265	7
+ 650	2
+ 356	8
+ 25	3
+ 266	6
+ 824	5
+ 436	8
+ 754	6
+ 345	2
+ 114	5
+ 471	9
+ 356	6
+ 726	4
+ 644	6
+ 751	7
+ 829	0
+ 383	5
+ 201	7
+ 291	2
+ 53	6
+ 836	9
+ 11	3
+ 628	8
+ 834	10
+ 972	9
+ 433	4
+ 875	8
+ 63	6
+ 170	7
+ 178	9
+ 359	0
+ 937	7
+ 487	1
+ 481	8
+ 365	5
+ 335	2
+ 411	3
+ 472	0
+ 112	3
+ 13	1
+ 254	4
+ 526	1
+ 236	6
+ 730	4
+ 297	9
+ 327	7
+ 916	3
+ 399	4
+ 402	9
+ 181	8
+ 414	5
+ 967	8
+ 862	4
+ 865	10
+ 745	9
+ 58	10
+ 325	6
+ 128	6
+ 173	9
+ 967	5
+ 767	3
+ 127	7
+ 558	5
+ 86	10
+ 405	3
+ 726	8
+ 783	7
+ 645	6
+ 132	5
+ 619	9
+ 388	7
+ 876	7
+ 260	0
+ 273	4
+ 862	2
+ 903	6
+ 534	0
+ 312	1
+ 555	4
+ 51	10
+ 665	8
+ 780	4
+ 469	4
+ 93	6
+ 934	7
+ 477	3
+ 388	4
+ 34	6
+ 356	3
+ 81	2
+ 546	10
+ 847	1
+ 15	2
+ 171	6
+ 558	2
+ 531	2
+ 998	3
+ 672	5
+ 735	8
+ 67	7
+ 476	5
+ 991	9
+ 897	0
+ 512	3
+ 332	6
+ 471	9
+ 578	3
+ 958	6
+ 477	1
+ 163	0
+ 351	7
+ 259	3
+ 4	9
+ 816	7
+ 695	9
+ 409	2
+ 427	4
+ 35	3
+ 426	5
+ 576	8
+ 140	0
+ 636	7
+ 365	6
+ 311	8
+ 724	5
+ 877	1
+ 167	1
+ 424	2
+ 67	2
+ 911	8
+ 122	3
+ 932	5
+ 721	10
+ 872	1
+ 514	4
+ 905	7
+ 495	5
+ 372	9
+ 135	7
+ 702	9
+ 156	6
+ 933	3
+ 715	4
+ 495	8
+ 596	4
+ 543	7
+ 726	5
+ 267	4
+ 442	1
+ 595	10
+ 588	5
+ 610	1
+ 40	10
+ 943	2
+ 665	6
+ 34	8
+ 224	10
+ 145	9
+ 324	6
+ 721	9
+ 45	3
+ 638	8
+ 739	9
+ 219	2
+ 45	8
+ 138	6
+ 314	7
+ 717	4
+ 730	7
+ 529	4
+ 305	6
+ 216	5
+ 531	4
+ 468	9
+ 0	2
+ 776	0
+ 453	4
+ 817	2
+ 320	0
+ 373	4
+ 850	5
+ 998	2
+ 259	7
+ 518	10
+ 375	0
+ 384	7
+ 611	6
+ 209	1
+ 961	7
+ 998	10
+ 866	8
+ 7	3
+ 188	8
+ 511	5
+ 861	9
+ 873	7
+ 395	9
+ 875	7
+ 586	4
+ 643	10
+ 441	0
+ 642	1
+ 628	9
+ 195	6
+ 529	2
+ 551	4
+ 967	6
+ 714	2
+ 383	2
+ 663	2
+ 109	5
+ 955	5
+ 408	8
+ 158	10
+ 223	8
+ 956	7
+ 829	6
+ 717	5
+ 449	9
+ 46	10
+ 104	6
+ 373	1
+ 156	1
+ 226	5
+ 313	9
+ 781	4
+ 426	7
+ 926	8
+ 566	1
+ 830	8
+ 886	8
+ 454	7
+ 384	2
+ 172	8
+ 82	2
+ 811	2
+ 816	2
+ 257	10
+ 272	5
+ 509	6
+ 373	3
+ 6	8
+ 27	9
+ 635	6
+ 16	5
+ 382	9
+ 250	8
+ 617	6
+ 7	8
+ 468	1
+ 7	3
+ 275	8
+ 464	5
+ 794	7
+ 16	3
+ 320	4
+ 593	3
+ 189	6
+ 259	8
+ 213	3
+ 288	6
+ 177	5
+ 431	8
+ 173	4
+ 583	6
+ 527	6
+ 920	8
+ 413	4
+ 335	2
+ 120	4
+ 509	4
+ 740	1
+ 767	9
+ 722	0
+ 754	9
+ 301	0
+ 530	5
+ 581	10
+ 273	8
+ 400	9
+ 395	9
+ 446	3
+ 728	9
+ 700	1
+ 65	8
+ 414	6
+ 260	2
+ 676	0
+ 84	4
+ 52	8
+ 334	4
+ 880	9
+ 832	5
+ 826	1
+ 216	2
+ 960	6
+ 152	4
+ 927	9
+ 265	6
+ 943	3
+ 446	4
+ 904	7
+ 511	6
+ 733	6
+ 979	8
+ 433	3
+ 138	3
+ 177	10
+ 775	0
+ 74	10
+ 227	0
+ 603	4
+ 441	5
+ 259	7
+ 157	2
+ 37	6
+ 559	9
+ 309	1
+ 522	0
+ 666	5
+ 827	1
+ 814	10
+ 413	10
+ 935	2
+ 993	0
+ 180	2
+ 44	8
+ 599	5
+ 312	9
+ 191	5
+ 61	2
+ 73	6
+ 169	4
+ 691	7
+ 424	4
+ 192	3
+ 456	0
+ 217	9
+ 997	2
+ 57	10
+ 162	2
+ 210	2
+ 19	8
+ 690	3
+ 668	9
+ 801	7
+ 109	9
+ 350	3
+ 256	0
+ 969	7
+ 399	2
+ 932	9
+ 168	1
+ 724	2
+ 301	8
+ 154	5
+ 19	4
+ 668	0
+ 173	4
+ 370	8
+ 239	2
+ 570	3
+ 45	9
+ 626	3
+ 962	6
+ 982	4
+ 757	9
+ 216	9
+ 63	9
+ 89	4
+ 722	2
+ 828	7
+ 606	5
+ 779	8
+ 854	1
+ 619	1
+ 320	2
+ 441	4
+ 110	1
+ 666	1
+ 663	6
+ 432	4
+ 562	6
+ 344	6
+ 588	4
+ 989	3
+ 676	8
+ 51	3
+ 313	8
+ 61	2
+ 978	7
+ 260	3
+ 870	7
+ 663	10
+ 768	3
+ 50	4
+ 978	5
+ 850	5
+ 129	2
+ 165	7
+ 628	2
+ 27	3
+ 971	1
+ 586	3
+ 907	6
+ 450	9
+ 327	7
+ 184	2
+ 410	8
+ 175	2
+ 177	2
+ 608	2
+ 707	5
+ 694	8
+ 652	9
+ 554	3
+ 13	6
+ 584	10
+ 658	2
+ 267	6
+ 816	7
+ 450	1
+ 428	6
+ 339	8
+ 480	5
+ 16	7
+ 739	6
+ 811	4
+ 82	5
+ 283	7
+ 364	8
+ 15	4
+ 417	6
+ 360	1
+ 769	6
+ 640	6
+ 345	1
+ 728	8
+ 723	1
+ 611	2
+ 581	6
+ 861	3
+ 252	7
+ 767	3
+ 177	1
+ 69	5
+ 887	1
+ 918	3
+ 684	3
+ 380	5
+ 906	0
+ 38	3
+ 110	8
+ 24	8
+ 833	6
+ 37	4
+ 263	9
+ 733	5
+ 570	5
+ 849	7
+ 550	9
+ 288	4
+ 2	2
+ 742	7
+ 484	1
+ 139	4
+ 142	2
+ 640	3
+ 942	7
+ 85	8
+ 300	1
+ 188	6
+ 20	9
+ 76	6
+ 422	9
+ 336	10
+ 843	6
+ 409	8
+ 830	2
+ 531	3
+ 274	7
+ 704	4
+ 846	3
+ 667	8
+ 8	8
+ 564	3
+ 874	8
+ 870	9
+ 674	9
+ 483	1
+ 871	8
+ 68	7
+ 444	5
+ 559	3
+ 629	1
+ 588	9
+ 760	3
+ 318	6
+ 636	10
+ 395	6
+ 737	10
+ 951	6
+ 711	8
+ 505	4
+ 767	10
+ 480	6
+ 807	5
+ 353	3
+ 25	9
+ 525	7
+ 2	1
+ 555	8
+ 405	9
+ 369	0
+ 858	8
+ 684	6
+ 723	6
+ 207	4
+ 456	7
+ 818	2
+ 701	3
+ 862	5
+ 845	2
+ 759	9
+ 127	3
+ 523	1
+ 397	1
+ 892	8
+ 952	3
+ 841	8
+ 25	5
+ 406	7
+ 160	6
+ 181	6
+ 325	10
+ 840	0
+ 297	7
+ 534	1
+ 916	3
+ 13	0
+ 577	5
+ 172	10
+ 615	1
+ 775	6
+ 325	6
+ 377	3
+ 141	8
+ 97	3
+ 396	3
+ 918	7
+ 278	8
+ 747	6
+ 459	3
+ 717	4
+ 574	7
+ 418	2
+ 265	6
+ 125	9
+ 655	9
+ 447	10
+ 516	8
+ 329	7
+ 606	4
+ 959	0
+ 705	9
+ 723	10
+ 634	5
+ 557	1
+ 751	3
+ 468	3
+ 4	9
+ 477	3
+ 477	6
+ 149	1
+ 501	6
+ 111	0
+ 419	4
+ 674	0
+ 867	6
+ 28	6
+ 509	8
+ 554	1
+ 221	1
+ 236	10
+ 385	7
+ 298	4
+ 590	8
+ 657	1
+ 375	8
+ 200	9
+ 402	3
+ 893	8
+ 752	6
+ 847	6
+ 199	9
+ 190	7
+ 626	7
+ 852	8
+ 854	1
+ 819	2
+ 792	1
+ 627	4
+ 891	3
+ 450	3
+ 91	6
+ 142	5
+ 961	0
+ 315	7
+ 602	2
+ 331	8
+ 37	5
+ 510	7
+ 265	4
+ 509	1
+ 450	3
+ 358	2
+ 445	10
+ 624	3
+ 270	1
+ 602	4
+ 724	7
+ 854	7
+ 779	2
+ 397	4
+ 331	7
+ 182	4
+ 248	7
+ 31	5
+ 55	5
+ 632	5
+ 869	10
+ 746	3
+ 976	4
+ 649	2
+ 445	3
+ 606	2
+ 995	5
+ 853	8
+ 629	2
+ 155	10
+ 977	3
+ 329	2
+ 30	4
+ 738	1
+ 901	4
+ 589	8
+ 361	3
+ 84	3
+ 706	7
+ 582	2
+ 984	2
+ 319	10
+ 648	2
+ 753	3
+ 421	9
+ 237	4
+ 246	6
+ 623	3
+ 926	4
+ 361	8
+ 732	9
+ 597	1
+ 285	7
+ 430	10
+ 414	0
+ 141	4
+ 201	5
+ 378	8
+ 631	1
+ 126	1
+ 40	4
+ 449	3
+ 929	1
+ 562	9
+ 433	9
+ 682	2
+ 872	3
+ 258	2
+ 960	7
+ 147	4
+ 700	3
+ 773	9
+ 747	2
+ 749	4
+ 282	9
+ 429	3
+ 239	9
+ 608	2
+ 949	2
+ 23	4
+ 92	7
+ 546	10
+ 984	8
+ 121	9
+ 491	3
+ 318	2
+ 556	1
+ 91	3
+ 242	8
+ 680	5
+ 716	1
+ 846	10
+ 986	5
+ 123	9
+ 624	1
+ 317	7
+ 851	9
+ 681	8
+ 668	8
+ 778	2
+ 71	1
+ 350	6
+ 186	4
+ 929	4
+ 282	6
+ 952	10
+ 718	8
+ 952	7
+ 252	1
+ 639	9
+ 221	10
+ 593	1
+ 820	3
+ 906	5
+ 76	7
+ 647	1
+ 779	10
+ 774	10
+ 438	7
+ 393	7
+ 312	3
+ 718	0
+ 143	7
+ 734	4
+ 745	4
+ 271	10
+ 330	9
+ 37	1
+ 138	9
+ 637	2
+ 627	3
+ 361	4
+ 281	1
+ 372	7
+ 838	8
+ 440	1
+ 110	2
+ 180	3
+ 828	9
+ 647	6
+ 287	9
+ 538	6
+ 782	6
+ 766	9
+ 518	4
+ 133	1
+ 688	5
+ 551	10
+ 629	9
+ 689	5
+ 688	1
+ 616	8
+ 287	8
+ 50	1
+ 709	7
+ 687	10
+ 616	2
+ 613	4
+ 801	4
+ 316	3
+ 782	4
+ 464	5
+ 944	0
+ 439	6
+ 939	1
+ 39	6
+ 257	7
+ 425	5
+ 451	5
+ 658	2
+ 173	3
+ 157	8
+ 570	8
+ 186	4
+ 148	5
+ 690	9
+ 951	2
+ 400	9
+ 170	8
+ 468	1
+ 967	5
+ 735	2
+ 162	2
+ 768	6
+ 635	4
+ 774	8
+ 771	9
+ 596	3
+ 700	8
+ 712	8
+ 283	4
+ 778	2
+ 556	2
+ 129	7
+ 17	6
+ 834	10
+ 104	6
+ 208	3
+ 729	10
+ 879	4
+ 403	7
+ 171	2
+ 583	8
+ 516	3
+ 548	2
+ 131	8
+ 631	9
+ 66	2
+ 86	2
+ 912	1
+ 792	7
+ 86	9
+ 315	3
+ 161	0
+ 272	0
+ 408	7
+ 693	6
+ 850	3
+ 346	4
+ 559	9
+ 595	7
+ 726	2
+ 598	8
+ 412	7
+ 987	3
+ 786	8
+ 70	9
+ 676	4
+ 167	8
+ 430	4
+ 877	8
+ 113	6
+ 417	10
+ 846	8
+ 330	4
+ 657	9
+ 94	4
+ 150	7
+ 175	6
+ 376	2
+ 886	2
+ 942	10
+ 34	6
+ 342	2
+ 455	8
+ 639	3
+ 610	8
+ 902	0
+ 715	7
+ 789	0
+ 152	4
+ 969	2
+ 829	1
+ 938	0
+ 682	3
+ 166	6
+ 475	1
+ 525	5
+ 725	9
+ 709	2
+ 639	3
+ 511	2
+ 99	4
+ 275	8
+ 160	1
+ 859	3
+ 509	8
+ 558	3
+ 948	5
+ 341	6
+ 809	5
+ 198	3
+ 614	7
+ 792	3
+ 590	5
+ 519	2
+ 848	0
+ 478	9
+ 443	8
+ 761	6
+ 816	6
+ 916	3
+ 448	5
+ 663	4
+ 970	0
+ 26	8
+ 512	2
+ 62	1
+ 947	9
+ 465	5
+ 354	10
+ 766	2
+ 14	2
+ 149	5
+ 996	9
+ 61	8
+ 530	10
+ 138	10
+ 451	8
+ 374	4
+ 806	4
+ 199	3
+ 623	3
+ 443	6
+ 115	9
+ 107	5
+ 893	9
+ 671	9
+ 117	8
+ 365	1
+ 730	4
+ 926	3
+ 403	1
+ 237	9
+ 865	6
+ 275	7
+ 10	5
+ 988	6
+ 736	4
+ 204	9
+ 340	3
+ 321	2
+ 185	10
+ 140	3
+ 812	5
+ 416	5
+ 931	3
+ 802	3
+ 405	0
+ 189	3
+ 650	5
+ 941	7
+ 939	9
+ 295	7
+ 361	5
+ 526	7
+ 810	8
+ 934	10
+ 839	1
+ 297	7
+ 579	7
+ 194	5
+ 54	10
+ 845	5
+ 36	0
+ 730	7
+ 498	7
+ 346	4
+ 602	6
+ 112	10
+ 140	6
+ 665	9
+ 486	6
+ 945	3
+ 673	2
+ 977	3
+ 954	2
+ 763	0
+ 167	6
+ 468	2
+ 641	2
+ 888	1
+ 870	2
+ 576	5
+ 876	7
+ 434	0
+ 326	1
+ 965	8
+ 698	9
+ 136	4
+ 151	1
+ 624	1
+ 284	4
+ 114	5
+ 994	6
+ 654	6
+ 780	5
+ 773	7
+ 777	3
+ 122	7
+ 36	6
+ 669	4
+ 655	6
+ 174	4
+ 544	3
+ 724	7
+ 423	3
+ 801	7
+ 734	9
+ 158	7
+ 497	8
+ 362	3
+ 354	1
+ 928	1
+ 484	0
+ 784	5
+ 605	5
+ 882	3
+ 87	1
+ 613	6
+ 365	3
+ 326	8
+ 685	1
+ 495	4
+ 42	7
+ 148	5
+ 465	5
+ 816	8
+ 646	7
+ 950	1
+ 793	7
+ 649	4
+ 187	5
+ 658	3
+ 587	3
+ 904	10
+ 608	2
+ 740	3
+ 356	2
+ 712	4
+ 888	9
+ 937	4
+ 370	8
+ 172	0
+ 497	1
+ 146	3
+ 855	8
+ 687	0
+ 327	3
+ 315	9
+ 616	2
+ 866	2
+ 449	6
+ 516	8
+ 842	2
+ 203	7
+ 89	1
+ 83	5
+ 893	3
+ 475	4
+ 376	6
+ 679	2
+ 416	4
+ 272	7
+ 711	6
+ 656	3
+ 806	5
+ 550	3
+ 129	1
+ 60	10
+ 295	3
+ 701	4
+ 403	8
+ 843	3
+ 39	3
+ 686	4
+ 940	4
+ 645	4
+ 732	9
+ 99	4
+ 504	8
+ 770	3
+ 278	3
+ 565	4
+ 386	6
+ 377	7
+ 887	1
+ 65	3
+ 861	9
+ 586	9
+ 227	3
+ 315	2
+ 638	10
+ 523	4
+ 878	6
+ 812	4
+ 378	6
+ 693	7
+ 901	3
+ 62	3
+ 881	4
+ 968	8
+ 517	0
+ 58	4
+ 941	6
+ 278	2
+ 917	6
+ 335	6
+ 553	9
+ 923	4
+ 480	7
+ 813	9
+ 316	5
+ 514	2
+ 763	6
+ 504	6
+ 16	5
+ 413	5
+ 505	5
+ 911	4
+ 116	2
+ 614	0
+ 782	9
+ 587	3
+ 806	5
+ 767	3
+ 246	6
+ 145	6
+ 86	7
+ 780	8
+ 236	3
+ 494	3
+ 756	9
+ 785	3
+ 378	7
+ 707	5
+ 885	3
+ 527	7
+ 269	1
+ 4	1
+ 625	8
+ 362	9
+ 351	5
+ 433	4
+ 166	2
+ 287	4
+ 497	8
+ 655	3
+ 688	4
+ 514	1
+ 137	2
+ 561	0
+ 541	1
+ 690	8
+ 202	7
+ 885	8
+ 464	2
+ 698	8
+ 753	1
+ 252	9
+ 345	5
+ 322	8
+ 321	10
+ 95	0
+ 418	6
+ 76	6
+ 829	6
+ 576	4
+ 725	3
+ 180	9
+ 959	1
+ 755	4
+ 311	5
+ 238	1
+ 585	5
+ 983	9
+ 30	3
+ 772	4
+ 283	9
+ 360	7
+ 476	4
+ 256	3
+ 73	8
+ 675	8
+ 98	9
+ 726	1
+ 919	5
+ 480	2
+ 934	7
+ 293	5
+ 208	3
+ 450	2
+ 582	2
+ 588	9
+ 88	9
+ 567	6
+ 384	8
+ 869	5
+ 655	5
+ 255	8
+ 398	10
+ 810	3
+ 461	3
+ 546	4
+ 8	8
+ 915	2
+ 115	4
+ 453	7
+ 586	0
+ 562	7
+ 988	1
+ 238	4
+ 952	1
+ 829	6
+ 650	1
+ 359	0
+ 64	2
+ 365	5
+ 459	9
+ 920	5
+ 749	8
+ 683	9
+ 200	1
+ 561	8
+ 176	1
+ 460	2
+ 252	7
+ 537	2
+ 805	4
+ 810	5
+ 449	2
+ 503	5
+ 338	9
+ 37	8
+ 778	10
+ 264	5
+ 793	9
+ 390	10
+ 83	10
+ 778	3
+ 74	2
+ 424	3
+ 936	10
+ 530	7
+ 326	3
+ 196	8
+ 509	7
+ 287	8
+ 567	3
+ 645	3
+ 282	9
+ 871	1
+ 856	3
+ 68	9
+ 212	8
+ 198	3
+ 84	6
+ 613	0
+ 583	1
+ 761	9
+ 484	10
+ 684	10
+ 657	10
+ 841	2
+ 296	5
+ 569	6
+ 395	4
+ 653	3
+ 702	7
+ 190	9
+ 567	4
+ 201	7
+ 11	8
+ 670	6
+ 957	4
+ 504	4
+ 389	2
+ 435	0
+ 160	3
+ 270	5
+ 761	8
+ 34	2
+ 279	7
+ 407	10
+ 408	6
+ 894	10
+ 986	1
+ 625	10
+ 908	3
+ 592	9
+ 727	1
+ 307	1
+ 285	7
+ 162	4
+ 17	4
+ 900	8
+ 270	9
+ 934	5
+ 622	3
+ 529	0
+ 939	4
+ 5	9
+ 518	6
+ 923	4
+ 925	5
+ 292	7
+ 612	6
+ 768	9
+ 341	9
+ 341	4
+ 362	2
+ 136	6
+ 175	1
+ 181	8
+ 411	7
+ 826	4
+ 134	8
+ 275	7
+ 461	2
+ 79	4
+ 714	4
+ 38	3
+ 970	8
+ 222	3
+ 737	6
+ 668	1
+ 803	8
+ 732	10
+ 873	9
+ 774	3
+ 623	6
+ 635	8
+ 431	9
+ 409	9
+ 108	5
+ 278	8
+ 858	3
+ 147	8
+ 123	4
+ 139	9
+ 931	8
+ 959	7
+ 611	7
+ 712	5
+ 604	5
+ 769	2
+ 86	4
+ 985	5
+ 313	4
+ 408	4
+ 881	7
+ 243	7
+ 2	4
+ 568	1
+ 760	7
+ 985	7
+ 514	9
+ 426	1
+ 636	1
+ 608	2
+ 624	4
+ 467	7
+ 780	5
+ 227	1
+ 846	6
+ 514	7
+ 321	8
+ 467	3
+ 148	0
+ 448	9
+ 742	4
+ 598	3
+ 378	0
+ 380	0
+ 162	10
+ 253	8
+ 365	7
+ 495	1
+ 173	7
+ 238	0
+ 356	8
+ 746	7
+ 510	2
+ 0	7
+ 248	4
+ 565	10
+ 882	2
+ 246	3
+ 187	6
+ 272	3
+ 614	5
+ 134	10
+ 246	6
+ 125	4
+ 350	4
+ 437	7
+ 115	2
+ 384	6
+ 396	4
+ 282	6
+ 833	8
+ 634	7
+ 10	9
+ 973	2
+ 507	2
+ 545	1
+ 771	7
+ 100	0
+ 307	2
+ 435	7
+ 588	9
+ 364	7
+ 55	7
+ 327	5
+ 132	6
+ 95	10
+ 455	7
+ 679	5
+ 609	7
+ 661	1
+ 897	2
+ 237	7
+ 885	3
+ 685	2
+ 563	1
+ 849	2
+ 991	2
+ 853	0
+ 961	2
+ 497	1
+ 788	6
+ 57	2
+ 320	7
+ 708	9
+ 387	4
+ 46	3
+ 575	3
+ 953	5
+ 620	6
+ 652	2
+ 758	5
+ 333	7
+ 714	2
+ 795	7
+ 365	3
+ 767	2
+ 883	8
+ 396	2
+ 559	1
+ 133	9
+ 472	2
+ 232	0
+ 461	2
+ 507	1
+ 823	2
+ 264	6
+ 659	6
+ 329	4
+ 783	1
+ 47	1
+ 416	8
+ 300	3
+ 638	7
+ 502	2
+ 800	6
+ 145	3
+ 814	4
+ 319	3
+ 561	8
+ 356	4
+ 984	6
+ 964	6
+ 218	3
+ 16	0
+ 418	1
+ 148	8
+ 878	4
+ 133	5
+ 144	6
+ 714	9
+ 270	9
+ 217	1
+ 235	5
+ 358	8
+ 362	7
+ 180	3
+ 335	1
+ 989	6
+ 437	0
+ 553	9
+ 69	7
+ 689	9
+ 149	8
+ 463	3
+ 457	2
+ 238	7
+ 36	5
+ 810	3
+ 990	2
+ 67	4
+ 883	2
+ 698	2
+ 390	7
+ 771	8
+ 693	3
+ 683	8
+ 26	4
+ 709	2
+ 194	2
+ 469	7
+ 349	7
+ 377	4
+ 161	2
+ 656	2
+ 355	7
+ 503	2
+ 969	2
+ 456	4
+ 889	2
+ 187	6
+ 553	9
+ 344	6
+ 241	1
+ 754	4
+ 225	2
+ 85	6
+ 929	5
+ 960	1
+ 650	6
+ 241	0
+ 339	7
+ 244	3
+ 946	7
+ 668	8
+ 928	9
+ 418	5
+ 725	8
+ 59	10
+ 815	8
+ 400	0
+ 35	5
+ 614	10
+ 948	6
+ 53	6
+ 190	3
+ 604	5
+ 39	8
+ 838	10
+ 547	5
+ 820	5
+ 361	2
+ 955	1
+ 0	0
+ 52	8
+ 826	5
+ 855	9
+ 938	5
+ 825	9
+ 43	9
+ 484	2
+ 173	1
+ 762	2
+ 935	6
+ 196	5
+ 107	0
+ 957	5
+ 254	9
+ 554	3
+ 926	6
+ 69	8
+ 58	9
+ 614	10
+ 393	4
+ 882	4
+ 317	4
+ 669	5
+ 454	4
+ 701	4
+ 32	9
+ 871	1
+ 913	8
+ 607	2
+ 740	2
+ 421	7
+ 767	5
+ 418	8
+ 414	0
+ 821	8
+ 470	7
+ 244	8
+ 69	9
+ 277	5
+ 345	10
+ 912	4
+ 875	8
+ 516	8
+ 611	1
+ 956	4
+ 285	4
+ 16	1
+ 867	4
+ 878	3
+ 466	7
+ 88	9
+ 401	3
+ 723	5
+ 245	0
+ 992	6
+ 978	9
+ 967	9
+ 687	5
+ 642	3
+ 607	6
+ 648	9
+ 974	7
+ 944	8
+ 98	8
+ 122	6
+ 520	2
+ 500	9
+ 541	2
+ 391	8
+ 223	4
+ 376	2
+ 288	3
+ 55	10
+ 827	7
+ 273	4
+ 294	9
+ 325	3
+ 585	3
+ 108	7
+ 92	2
+ 248	6
+ 440	7
+ 533	10
+ 971	9
+ 767	2
+ 308	1
+ 395	6
+ 487	4
+ 571	3
+ 146	8
+ 747	4
+ 765	1
+ 707	4
+ 342	8
+ 34	4
+ 46	3
+ 46	5
+ 30	6
+ 466	0
+ 504	2
+ 194	8
+ 378	6
+ 408	9
+ 38	10
+ 178	2
+ 823	9
+ 624	6
+ 997	3
+ 939	3
+ 147	10
+ 772	2
+ 255	8
+ 677	3
+ 397	1
+ 286	9
+ 378	5
+ 712	8
+ 69	1
+ 620	1
+ 98	8
+ 291	9
+ 722	9
+ 509	7
+ 245	4
+ 58	4
+ 421	8
+ 584	7
+ 648	3
+ 962	0
+ 405	2
+ 945	8
+ 727	7
+ 538	8
+ 776	2
+ 903	9
+ 956	2
+ 796	7
+ 108	3
+ 397	4
+ 753	5
+ 745	2
+ 285	3
+ 850	9
+ 591	8
+ 977	10
+ 59	9
+ 780	8
+ 578	3
+ 583	4
+ 476	5
+ 228	4
+ 679	0
+ 110	8
+ 329	5
+ 141	1
+ 963	9
+ 255	2
+ 215	1
+ 181	8
+ 917	2
+ 803	10
+ 80	6
+ 763	7
+ 900	3
+ 12	4
+ 831	2
+ 809	5
+ 264	9
+ 297	6
+ 427	4
+ 674	4
+ 324	9
+ 638	5
+ 34	8
+ 346	10
+ 978	1
+ 928	1
+ 730	7
+ 716	6
+ 36	7
+ 7	9
+ 969	8
+ 378	2
+ 735	7
+ 826	2
+ 113	5
+ 552	4
+ 429	2
+ 976	5
+ 10	3
+ 415	10
+ 470	3
+ 46	2
+ 33	8
+ 831	1
+ 490	8
+ 937	5
+ 654	3
+ 691	4
+ 990	5
+ 550	1
+ 17	1
+ 539	4
+ 292	5
+ 909	3
+ 837	3
+ 289	3
+ 666	3
+ 507	7
+ 96	3
+ 769	6
+ 176	7
+ 45	8
+ 21	7
+ 218	0
+ 253	8
+ 113	3
+ 870	7
+ 715	2
+ 167	6
+ 463	0
+ 947	8
+ 312	6
+ 87	8
+ 312	2
+ 157	1
+ 770	3
+ 787	8
+ 163	8
+ 551	4
+ 818	8
+ 150	9
+ 74	0
+ 583	8
+ 182	8
+ 414	6
+ 755	4
+ 397	1
+ 974	5
+ 886	3
+ 668	0
+ 368	4
+ 377	2
+ 253	5
+ 964	8
+ 921	8
+ 609	1
+ 713	7
+ 90	3
+ 472	3
+ 47	9
+ 917	8
+ 247	3
+ 869	2
+ 798	8
+ 508	5
+ 798	9
+ 905	2
+ 32	2
+ 714	10
+ 962	6
+ 777	6
+ 705	5
+ 253	8
+ 787	7
+ 67	8
+ 612	10
+ 636	9
+ 298	5
+ 80	1
+ 259	6
+ 563	1
+ 464	5
+ 232	5
+ 625	9
+ 491	6
+ 581	3
+ 157	3
+ 759	4
+ 82	5
+ 136	1
+ 380	7
+ 132	0
+ 607	4
+ 520	7
+ 526	8
+ 275	1
+ 837	7
+ 556	1
+ 235	2
+ 15	7
+ 768	6
+ 994	9
+ 882	8
+ 336	10
+ 299	5
+ 112	7
+ 220	2
+ 695	8
+ 675	2
+ 513	2
+ 995	8
+ 290	8
+ 527	8
+ 900	8
+ 27	9
+ 488	8
+ 510	5
+ 720	4
+ 235	1
+ 355	5
+ 528	5
+ 213	7
+ 711	9
+ 574	4
+ 122	1
+ 587	1
+ 876	9
+ 948	4
+ 723	8
+ 165	7
+ 764	7
+ 545	3
+ 134	3
+ 666	4
+ 321	0
+ 903	8
+ 489	1
+ 597	2
+ 23	2
+ 586	1
+ 259	2
+ 263	1
+ 50	2
+ 537	8
+ 60	7
+ 522	8
+ 354	1
+ 98	5
+ 331	8
+ 857	7
+ 786	8
+ 501	3
+ 876	1
+ 475	9
+ 269	1
+ 45	5
+ 234	3
+ 662	3
+ 518	2
+ 56	6
+ 900	6
+ 402	3
+ 644	5
+ 743	10
+ 264	6
+ 627	1
+ 360	1
+ 325	2
+ 226	8
+ 134	5
+ 861	2
+ 22	1
+ 486	7
+ 379	0
+ 882	4
+ 582	8
+ 12	10
+ 37	7
+ 484	8
+ 631	7
+ 379	3
+ 799	7
+ 387	1
+ 974	6
+ 925	1
+ 108	8
+ 287	1
+ 881	8
+ 813	3
+ 778	7
+ 695	4
+ 478	7
+ 344	5
+ 364	8
+ 294	10
+ 577	7
+ 254	4
+ 412	6
+ 500	4
+ 254	4
+ 495	4
+ 211	8
+ 491	1
+ 555	3
+ 352	3
+ 1000	0
+ 693	5
+ 755	0
+ 992	1
+ 865	3
+ 114	4
+ 959	4
+ 818	4
+ 9	3
+ 757	3
+ 743	3
+ 625	10
+ 34	1
+ 46	6
+ 421	4
+ 923	4
+ 445	6
+ 898	2
+ 653	9
+ 319	5
+ 175	4
+ 960	1
+ 802	8
+ 505	8
+ 96	3
+ 75	8
+ 515	7
+ 793	5
+ 817	8
+ 139	2
+ 236	1
+ 658	7
+ 677	6
+ 882	3
+ 445	2
+ 848	6
+ 635	8
+ 754	4
+ 586	3
+ 249	7
+ 523	3
+ 522	0
+ 24	3
+ 587	8
+ 153	7
+ 78	4
+ 787	7
+ 71	5
+ 291	10
+ 794	7
+ 155	6
+ 356	8
+ 451	1
+ 228	0
+ 370	5
+ 719	9
+ 801	2
+ 930	8
+ 556	5
+ 667	7
+ 242	7
+ 98	0
+ 481	2
+ 493	8
+ 123	3
+ 508	3
+ 929	9
+ 68	4
+ 974	3
+ 417	3
+ 772	1
+ 237	6
+ 378	2
+ 399	9
+ 683	1
+ 642	9
+ 811	7
+ 954	3
+ 910	4
+ 64	0
+ 734	6
+ 310	7
+ 437	4
+ 43	4
+ 674	5
+ 756	4
+ 596	10
+ 20	10
+ 158	4
+ 907	8
+ 485	5
+ 766	3
+ 290	7
+ 588	2
+ 167	7
+ 233	9
+ 224	5
+ 564	7
+ 922	6
+ 73	6
+ 67	8
+ 41	7
+ 820	1
+ 637	10
+ 480	5
+ 820	10
+ 94	6
+ 260	4
+ 306	8
+ 584	5
+ 500	8
+ 374	7
+ 361	9
+ 385	3
+ 545	5
+ 877	6
+ 286	9
+ 275	1
+ 979	9
+ 85	5
+ 457	9
+ 424	6
+ 492	7
+ 936	8
+ 531	5
+ 271	0
+ 337	6
+ 755	7
+ 583	1
+ 980	1
+ 599	9
+ 739	9
+ 776	0
+ 992	8
+ 926	1
+ 215	4
+ 982	6
+ 935	5
+ 322	9
+ 272	9
+ 391	5
+ 885	7
+ 189	6
+ 426	8
+ 780	4
+ 899	4
+ 264	6
+ 264	0
+ 652	3
+ 796	6
+ 332	0
+ 961	3
+ 649	9
+ 789	10
+ 767	1
+ 825	2
+ 605	7
+ 886	8
+ 349	3
+ 566	1
+ 719	5
+ 508	10
+ 103	8
+ 23	8
+ 28	8
+ 333	4
+ 830	3
+ 675	5
+ 190	5
+ 450	10
+ 525	3
+ 115	1
+ 984	0
+ 924	3
+ 313	5
+ 463	0
+ 955	10
+ 15	1
+ 743	0
+ 813	8
+ 858	1
+ 131	7
+ 440	8
+ 167	6
+ 270	6
+ 587	8
+ 892	7
+ 925	9
+ 702	8
+ 210	0
+ 339	7
+ 47	3
+ 643	1
+ 351	4
+ 101	2
+ 157	10
+ 310	3
+ 647	7
+ 93	8
+ 380	4
+ 432	10
+ 158	3
+ 668	1
+ 201	4
+ 933	4
+ 386	3
+ 83	4
+ 566	7
+ 496	9
+ 113	6
+ 81	3
+ 556	4
+ 557	2
+ 140	7
+ 16	5
+ 13	4
+ 487	2
+ 772	2
+ 253	10
+ 526	2
+ 384	9
+ 458	5
+ 345	0
+ 194	8
+ 941	3
+ 438	0
+ 577	10
+ 413	1
+ 196	6
+ 784	2
+ 74	8
+ 660	6
+ 967	4
+ 716	2
+ 405	2
+ 407	8
+ 154	9
+ 256	5
+ 888	4
+ 341	8
+ 757	8
+ 852	3
+ 771	3
+ 468	10
+ 819	3
+ 179	9
+ 49	8
+ 454	0
+ 271	2
+ 238	7
+ 413	6
+ 465	6
+ 509	7
+ 67	4
+ 171	4
+ 226	9
+ 186	1
+ 261	10
+ 343	7
+ 924	2
+ 982	1
+ 55	0
+ 942	5
+ 48	2
+ 679	3
+ 890	1
+ 930	4
+ 659	4
+ 75	7
+ 836	2
+ 133	1
+ 173	3
+ 141	4
+ 277	5
+ 164	2
+ 646	1
+ 305	7
+ 178	2
+ 210	2
+ 460	9
+ 512	4
+ 981	4
+ 705	6
+ 881	8
+ 366	7
+ 26	5
+ 780	2
+ 818	9
+ 634	1
+ 404	8
+ 296	0
+ 945	6
+ 751	1
+ 848	10
+ 349	3
+ 850	9
+ 658	8
+ 303	4
+ 471	2
+ 143	8
+ 902	2
+ 335	7
+ 368	2
+ 602	0
+ 248	0
+ 800	5
+ 55	7
+ 145	8
+ 868	10
+ 767	2
+ 301	6
+ 78	10
+ 447	4
+ 322	9
+ 566	5
+ 754	5
+ 633	1
+ 149	0
+ 242	8
+ 2	5
+ 757	8
+ 35	8
+ 547	2
+ 618	4
+ 174	4
+ 631	5
+ 1	7
+ 434	4
+ 91	8
+ 366	7
+ 221	1
+ 124	9
+ 208	3
+ 855	5
+ 25	9
+ 941	8
+ 660	10
+ 593	2
+ 157	2
+ 621	3
+ 596	3
+ 806	6
+ 962	2
+ 45	1
+ 996	4
+ 709	2
+ 530	8
+ 72	7
+ 107	9
+ 189	1
+ 784	1
+ 913	4
+ 106	5
+ 650	3
+ 717	3
+ 594	3
+ 524	4
+ 910	5
+ 640	10
+ 538	6
+ 365	2
+ 854	9
+ 80	9
+ 634	2
+ 852	8
+ 318	6
+ 953	2
+ 80	1
+ 737	7
+ 323	5
+ 2	9
+ 766	5
+ 317	7
+ 11	10
+ 630	5
+ 593	10
+ 795	4
+ 891	9
+ 372	5
+ 61	2
+ 348	4
+ 861	3
+ 610	9
+ 360	3
+ 672	7
+ 800	7
+ 599	6
+ 199	9
+ 242	2
+ 873	9
+ 759	5
+ 868	6
+ 912	8
+ 429	3
+ 284	5
+ 507	6
+ 869	4
+ 933	5
+ 309	3
+ 825	10
+ 976	6
+ 654	6
+ 190	9
+ 491	4
+ 63	4
+ 304	8
+ 829	2
+ 377	7
+ 931	8
+ 24	2
+ 295	5
+ 848	2
+ 899	8
+ 642	2
+ 74	5
+ 188	0
+ 92	8
+ 624	3
+ 695	1
+ 714	8
+ 479	0
+ 581	3
+ 191	10
+ 49	1
+ 763	1
+ 337	1
+ 604	2
+ 222	5
+ 965	9
+ 712	0
+ 332	9
+ 88	4
+ 742	7
+ 706	4
+ 828	4
+ 196	3
+ 438	8
+ 616	6
+ 735	7
+ 751	5
+ 738	1
+ 556	3
+ 272	8
+ 846	2
+ 643	6
+ 277	10
+ 457	4
+ 398	2
+ 77	1
+ 636	9
+ 524	8
+ 213	10
+ 609	8
+ 591	3
+ 493	3
+ 842	2
+ 430	4
+ 573	7
+ 177	4
+ 940	8
+ 977	2
+ 795	4
+ 581	2
+ 633	7
+ 297	3
+ 564	8
+ 101	8
+ 783	7
+ 605	4
+ 55	1
+ 716	9
+ 329	1
+ 296	9
+ 847	5
+ 321	8
+ 294	3
+ 3	1
+ 731	6
+ 281	4
+ 243	6
+ 634	8
+ 399	7
+ 583	2
+ 445	2
+ 555	5
+ 286	3
+ 398	6
+ 417	7
+ 517	3
+ 167	8
+ 51	5
+ 135	1
+ 549	9
+ 638	8
+ 231	9
+ 409	9
+ 687	8
+ 599	3
+ 989	0
+ 458	5
+ 545	7
+ 816	9
+ 359	2
+ 637	9
+ 496	8
+ 713	5
+ 265	8
+ 601	8
+ 715	2
+ 645	9
+ 119	1
+ 810	8
+ 862	4
+ 76	9
+ 454	5
+ 395	10
+ 279	2
+ 942	6
+ 442	6
+ 513	9
+ 383	2
+ 486	6
+ 73	1
+ 463	8
+ 325	1
+ 733	4
+ 162	5
+ 251	0
+ 952	3
+ 874	4
+ 862	3
+ 405	1
+ 479	3
+ 778	9
+ 925	3
+ 860	3
+ 516	3
+ 956	6
+ 433	4
+ 377	8
+ 527	1
+ 203	7
+ 654	5
+ 713	6
+ 781	6
+ 12	6
+ 856	4
+ 783	3
+ 763	6
+ 257	7
+ 852	1
+ 995	4
+ 463	10
+ 957	9
+ 369	3
+ 654	9
+ 445	9
+ 584	1
+ 310	3
+ 704	1
+ 884	7
+ 734	7
+ 132	5
+ 75	9
+ 79	3
+ 582	9
+ 449	6
+ 299	9
+ 527	3
+ 808	9
+ 590	5
+ 791	0
+ 318	4
+ 134	6
+ 671	8
+ 721	6
+ 554	5
+ 295	7
+ 973	4
+ 582	1
+ 702	2
+ 983	2
+ 741	3
+ 63	3
+ 538	9
+ 163	1
+ 333	10
+ 164	7
+ 329	3
+ 280	10
+ 136	0
+ 555	7
+ 455	8
+ 377	4
+ 220	10
+ 480	9
+ 122	5
+ 72	9
+ 744	1
+ 130	3
+ 6	3
+ 410	3
+ 248	6
+ 989	6
+ 872	3
+ 577	0
+ 270	1
+ 697	7
+ 981	1
+ 153	2
+ 32	6
+ 122	2
+ 96	2
+ 16	8
+ 329	1
+ 122	3
+ 440	5
+ 673	7
+ 107	7
+ 265	10
+ 932	8
+ 986	2
+ 972	7
+ 927	10
+ 757	1
+ 154	8
+ 713	3
+ 942	8
+ 470	10
+ 649	8
+ 104	8
+ 134	5
+ 305	8
+ 232	4
+ 469	5
+ 390	4
+ 338	4
+ 602	3
+ 58	5
+ 264	8
+ 609	4
+ 603	3
+ 694	5
+ 131	2
+ 503	8
+ 962	6
+ 552	1
+ 152	9
+ 902	4
+ 268	4
+ 881	7
+ 772	2
+ 33	4
+ 529	1
+ 903	8
+ 905	5
+ 210	5
+ 833	9
+ 53	10
+ 67	6
+ 744	0
+ 164	3
+ 125	3
+ 153	0
+ 699	4
+ 398	6
+ 78	2
+ 798	1
+ 544	3
+ 202	4
+ 119	1
+ 959	3
+ 615	8
+ 232	7
+ 756	3
+ 224	5
+ 328	4
+ 797	5
+ 703	10
+ 480	4
+ 371	9
+ 982	4
+ 49	8
+ 561	6
+ 106	8
+ 40	2
+ 869	10
+ 554	5
+ 790	8
+ 151	5
+ 85	4
+ 47	4
+ 763	8
+ 866	5
+ 794	3
+ 868	2
+ 225	8
+ 615	3
+ 629	2
+ 866	7
+ 937	9
+ 960	8
+ 904	5
+ 290	7
+ 301	4
+ 241	4
+ 816	3
+ 799	6
+ 131	7
+ 45	9
+ 12	9
+ 90	2
+ 762	7
+ 510	4
+ 880	4
+ 126	8
+ 282	1
+ 623	2
+ 601	9
+ 880	9
+ 354	1
+ 287	2
+ 408	1
+ 749	5
+ 753	8
+ 464	8
+ 707	6
+ 2	5
+ 258	5
+ 859	1
+ 888	10
+ 956	2
+ 71	6
+ 355	7
+ 492	2
+ 574	8
+ 355	9
+ 15	8
+ 948	8
+ 302	7
+ 558	8
+ 466	3
+ 320	5
+ 733	6
+ 980	6
+ 716	9
+ 577	7
+ 37	6
+ 251	4
+ 321	7
+ 627	9
+ 588	10
+ 756	6
+ 746	7
+ 367	0
+ 405	9
+ 814	9
+ 191	1
+ 338	9
+ 712	3
+ 517	4
+ 186	1
+ 100	2
+ 743	4
+ 615	1
+ 93	2
+ 958	7
+ 225	7
+ 284	10
+ 418	7
+ 19	8
+ 577	8
+ 693	8
+ 967	0
+ 692	7
+ 349	2
+ 106	5
+ 303	2
+ 758	0
+ 557	4
+ 109	7
+ 616	1
+ 332	8
+ 782	6
+ 812	2
+ 267	8
+ 22	8
+ 665	7
+ 612	6
+ 746	3
+ 309	1
+ 512	4
+ 630	8
+ 622	4
+ 860	2
+ 762	10
+ 830	4
+ 37	2
+ 219	8
+ 777	0
+ 19	0
+ 863	0
+ 888	5
+ 756	5
+ 159	5
+ 804	5
+ 597	3
+ 884	2
+ 131	5
+ 616	10
+ 685	4
+ 961	5
+ 756	10
+ 675	10
+ 818	5
+ 6	8
+ 496	9
+ 878	4
+ 397	6
+ 884	6
+ 135	7
+ 23	7
+ 3	9
+ 959	1
+ 412	6
+ 125	1
+ 953	1
+ 611	7
+ 84	3
+ 683	9
+ 739	7
+ 738	2
+ 559	6
+ 619	10
+ 249	5
+ 511	4
+ 190	5
+ 116	2
+ 442	1
+ 327	9
+ 649	5
+ 951	6
+ 538	6
+ 310	6
+ 848	10
+ 524	6
+ 684	3
+ 822	2
+ 878	4
+ 198	1
+ 943	7
+ 512	1
+ 244	6
+ 325	7
+ 702	7
+ 539	4
+ 104	5
+ 952	6
+ 52	3
+ 264	9
+ 257	8
+ 487	9
+ 50	3
+ 183	9
+ 748	4
+ 56	7
+ 91	6
+ 823	3
+ 195	1
+ 21	9
+ 801	6
+ 247	9
+ 50	2
+ 546	1
+ 462	8
+ 2	7
+ 597	5
+ 659	6
+ 797	8
+ 575	5
+ 224	6
+ 236	3
+ 198	1
+ 650	4
+ 208	7
+ 289	0
+ 231	5
+ 913	3
+ 735	5
+ 383	2
+ 268	4
+ 915	9
+ 874	6
+ 512	7
+ 417	1
+ 215	6
+ 718	5
+ 955	9
+ 511	6
+ 309	7
+ 275	6
+ 727	5
+ 133	6
+ 786	9
+ 99	2
+ 64	4
+ 554	10
+ 233	4
+ 554	7
+ 98	10
+ 832	3
+ 611	5
+ 765	6
+ 466	3
+ 170	8
+ 995	4
+ 371	7
+ 951	5
+ 363	7
+ 371	5
+ 907	4
+ 830	5
+ 414	1
+ 889	10
+ 808	10
+ 937	6
+ 301	5
+ 189	1
+ 114	7
+ 343	3
+ 429	3
+ 729	8
+ 61	7
+ 304	4
+ 416	7
+ 886	3
+ 110	7
+ 784	5
+ 779	7
+ 491	6
+ 660	4
+ 226	10
+ 976	4
+ 28	1
+ 71	4
+ 374	5
+ 709	1
+ 300	8
+ 782	6
+ 193	2
+ 280	1
+ 521	4
+ 794	3
+ 913	6
+ 978	4
+ 159	6
+ 833	4
+ 600	8
+ 801	6
+ 899	9
+ 999	3
+ 371	7
+ 376	7
+ 477	2
+ 276	7
+ 356	6
+ 749	9
+ 945	5
+ 183	9
+ 116	2
+ 262	3
+ 799	1
+ 661	4
+ 904	5
+ 28	8
+ 334	0
+ 76	7
+ 735	5
+ 376	2
+ 609	7
+ 882	10
+ 207	6
+ 843	2
+ 174	0
+ 10	3
+ 187	3
+ 565	10
+ 366	2
+ 386	3
+ 689	4
+ 73	0
+ 441	1
+ 727	2
+ 600	1
+ 388	2
+ 756	3
+ 176	10
+ 901	0
+ 115	1
+ 45	1
+ 364	2
+ 396	9
+ 218	8
+ 156	6
+ 32	8
+ 18	1
+ 867	5
+ 254	6
+ 635	9
+ 699	0
+ 65	5
+ 293	2
+ 417	2
+ 259	5
+ 268	3
+ 656	6
+ 535	1
+ 562	8
+ 814	7
+ 357	8
+ 563	4
+ 952	4
+ 834	2
+ 25	5
+ 60	7
+ 492	1
+ 178	8
+ 365	6
+ 977	6
+ 127	2
+ 928	8
+ 877	5
+ 834	4
+ 216	6
+ 157	6
+ 495	7
+ 949	4
+ 150	8
+ 653	2
+ 252	7
+ 898	7
+ 838	1
+ 527	2
+ 671	5
+ 827	8
+ 750	8
+ 581	6
+ 217	4
+ 66	4
+ 64	2
+ 7	6
+ 943	10
+ 6	1
+ 738	7
+ 267	10
+ 372	2
+ 733	2
+ 242	3
+ 413	9
+ 765	2
+ 712	5
+ 994	3
+ 142	2
+ 708	2
+ 645	8
+ 431	7
+ 331	4
+ 608	3
+ 466	3
+ 996	7
+ 336	4
+ 899	1
+ 577	1
+ 330	10
+ 54	1
+ 229	8
+ 609	2
+ 59	8
+ 435	8
+ 959	1
+ 539	4
+ 733	9
+ 763	3
+ 207	2
+ 687	2
+ 961	0
+ 570	9
+ 93	1
+ 1	4
+ 137	1
+ 517	4
+ 821	1
+ 590	9
+ 878	0
+ 646	8
+ 106	2
+ 226	8
+ 56	10
+ 180	3
+ 218	9
+ 466	2
+ 891	0
+ 39	10
+ 183	0
+ 407	3
+ 95	9
+ 686	9
+ 51	3
+ 795	9
+ 301	4
+ 765	4
+ 627	10
+ 246	7
+ 981	4
+ 946	2
+ 294	4
+ 378	2
+ 448	4
+ 170	6
+ 457	6
+ 950	6
+ 501	6
+ 467	6
+ 911	3
+ 480	2
+ 704	2
+ 619	3
+ 237	9
+ 14	2
+ 291	10
+ 416	6
+ 372	8
+ 770	8
+ 212	9
+ 451	7
+ 516	4
+ 221	0
+ 37	7
+ 568	9
+ 950	0
+ 160	7
+ 293	8
+ 985	5
+ 644	10
+ 747	9
+ 960	2
+ 519	3
+ 958	3
+ 151	2
+ 227	6
+ 838	7
+ 3	1
+ 760	0
+ 747	3
+ 988	7
+ 376	1
+ 351	7
+ 928	3
+ 198	6
+ 336	9
+ 506	3
+ 109	0
+ 627	1
+ 312	8
+ 236	5
+ 380	1
+ 283	4
+ 133	0
+ 423	9
+ 371	4
+ 577	7
+ 559	9
+ 415	5
+ 264	6
+ 58	6
+ 559	6
+ 896	7
+ 588	5
+ 734	9
+ 302	10
+ 440	7
+ 45	7
+ 66	2
+ 766	5
+ 58	1
+ 899	6
+ 883	5
+ 562	3
+ 945	8
+ 911	0
+ 427	5
+ 566	3
+ 138	2
+ 847	9
+ 55	1
+ 842	5
+ 832	9
+ 218	9
+ 66	10
+ 386	1
+ 120	3
+ 758	0
+ 743	3
+ 300	7
+ 147	2
+ 690	6
+ 681	3
+ 898	8
+ 411	7
+ 691	5
+ 895	5
+ 960	7
+ 420	2
+ 625	5
+ 162	0
+ 610	3
+ 296	4
+ 283	0
+ 688	6
+ 726	8
+ 794	4
+ 410	5
+ 673	3
+ 294	1
+ 54	10
+ 549	9
+ 518	5
+ 677	9
+ 687	3
+ 425	8
+ 314	0
+ 130	6
+ 402	4
+ 648	1
+ 997	4
+ 926	8
+ 791	3
+ 267	5
+ 645	6
+ 547	7
+ 546	1
+ 649	1
+ 605	3
+ 3	3
+ 628	4
+ 141	9
+ 462	3
+ 551	9
+ 684	2
+ 954	7
+ 574	9
+ 472	4
+ 217	7
+ 828	9
+ 299	4
+ 562	8
+ 471	2
+ 909	1
+ 536	9
+ 367	2
+ 339	5
+ 106	8
+ 779	7
+ 664	5
+ 856	6
+ 144	4
+ 499	6
+ 796	7
+ 353	6
+ 579	7
+ 999	1
+ 497	5
+ 351	4
+ 546	9
+ 317	9
+ 51	7
+ 421	2
+ 456	2
+ 813	1
+ 664	7
+ 738	8
+ 100	2
+ 422	9
+ 953	8
+ 520	5
+ 428	5
+ 672	9
+ 990	0
+ 331	5
+ 910	6
+ 448	10
+ 305	9
+ 118	8
+ 70	9
+ 881	7
+ 601	6
+ 541	7
+ 855	10
+ 597	8
+ 739	1
+ 341	2
+ 637	0
+ 93	6
+ 37	4
+ 162	9
+ 73	6
+ 908	4
+ 480	0
+ 139	6
+ 957	0
+ 284	6
+ 638	8
+ 259	5
+ 788	9
+ 302	5
+ 974	6
+ 695	6
+ 656	8
+ 237	7
+ 212	4
+ 639	3
+ 9	5
+ 663	5
+ 573	8
+ 39	5
+ 821	3
+ 88	5
+ 148	3
+ 952	9
+ 204	3
+ 464	2
+ 896	2
+ 789	6
+ 947	0
+ 244	2
+ 425	9
+ 444	4
+ 430	1
+ 924	0
+ 909	10
+ 533	7
+ 286	6
+ 189	4
+ 969	1
+ 370	2
+ 394	8
+ 350	3
+ 993	1
+ 842	9
+ 165	1
+ 99	6
+ 969	5
+ 24	4
+ 651	9
+ 401	6
+ 911	9
+ 290	2
+ 556	5
+ 631	5
+ 619	0
+ 696	0
+ 835	0
+ 303	8
+ 185	1
+ 767	3
+ 231	9
+ 940	2
+ 410	10
+ 598	1
+ 912	10
+ 621	8
+ 934	9
+ 20	5
+ 389	7
+ 14	0
+ 651	7
+ 22	5
+ 757	3
+ 313	9
+ 471	1
+ 292	7
+ 947	2
+ 902	4
+ 196	5
+ 418	1
+ 500	0
+ 931	4
+ 949	10
+ 924	3
+ 601	9
+ 348	3
+ 648	4
+ 738	4
+ 695	1
+ 347	2
+ 132	6
+ 867	1
+ 872	8
+ 436	1
+ 269	9
+ 176	8
+ 893	1
+ 203	8
+ 59	1
+ 181	7
+ 65	5
+ 912	7
+ 898	7
+ 118	6
+ 702	5
+ 758	8
+ 105	6
+ 913	10
+ 395	3
+ 45	7
+ 204	2
+ 433	1
+ 329	6
+ 939	4
+ 764	1
+ 48	8
+ 650	10
+ 542	5
+ 610	7
+ 141	3
+ 126	9
+ 146	2
+ 525	1
+ 208	9
+ 409	3
+ 584	6
+ 474	0
+ 710	8
+ 654	6
+ 190	4
+ 770	2
+ 247	4
+ 198	8
+ 968	8
+ 448	1
+ 121	6
+ 8	3
+ 805	5
+ 326	0
+ 452	7
+ 265	0
+ 347	7
+ 53	1
+ 542	7
+ 706	7
+ 124	5
+ 970	4
+ 896	2
+ 159	9
+ 977	6
+ 972	1
+ 182	10
+ 364	10
+ 513	7
+ 999	10
+ 425	3
+ 1000	8
+ 3	1
+ 829	5
+ 759	5
+ 277	9
+ 12	2
+ 254	9
+ 415	4
+ 772	4
+ 21	7
+ 490	2
+ 725	9
+ 189	2
+ 544	2
+ 202	10
+ 452	2
+ 741	5
+ 254	6
+ 1000	0
+ 106	3
+ 896	1
+ 523	1
+ 27	9
+ 563	8
+ 330	6
+ 544	8
+ 786	3
+ 674	10
+ 506	2
+ 162	7
+ 186	6
+ 910	9
+ 69	2
+ 496	1
+ 177	6
+ 346	1
+ 720	9
+ 223	7
+ 807	8
+ 546	1
+ 369	1
+ 958	2
+ 358	6
+ 129	9
+ 849	3
+ 573	0
+ 906	5
+ 961	10
+ 646	5
+ 45	8
+ 59	4
+ 896	8
+ 259	1
+ 526	1
+ 904	1
+ 204	3
+ 162	2
+ 428	5
+ 793	6
+ 385	6
+ 849	10
+ 676	8
+ 440	6
+ 731	1
+ 94	8
+ 909	2
+ 166	8
+ 933	4
+ 923	5
+ 492	8
+ 531	7
+ 100	7
+ 858	5
+ 214	7
+ 86	6
+ 292	9
+ 556	10
+ 691	10
+ 604	4
+ 82	7
+ 197	10
+ 851	4
+ 796	8
+ 788	7
+ 243	3
+ 547	8
+ 975	6
+ 467	8
+ 176	7
+ 484	3
+ 279	8
+ 198	8
+ 743	9
+ 832	3
+ 310	9
+ 46	5
+ 906	9
+ 871	7
+ 681	7
+ 422	9
+ 938	10
+ 698	9
+ 615	2
+ 747	8
+ 846	2
+ 53	1
+ 6	3
+ 961	7
+ 139	8
+ 97	4
+ 707	1
+ 957	6
+ 40	8
+ 314	7
+ 487	7
+ 645	4
+ 704	3
+ 339	3
+ 508	1
+ 110	4
+ 315	2
+ 479	3
+ 414	4
+ 70	6
+ 231	2
+ 3	9
+ 311	10
+ 550	4
+ 788	9
+ 72	3
+ 600	7
+ 700	3
+ 60	0
+ 623	6
+ 124	7
+ 922	4
+ 897	4
+ 760	3
+ 839	8
+ 864	1
+ 998	9
+ 9	3
+ 827	6
+ 660	6
+ 423	7
+ 891	0
+ 450	6
+ 327	5
+ 630	10
+ 78	8
+ 685	0
+ 194	6
+ 401	10
+ 893	2
+ 785	8
+ 311	8
+ 625	3
+ 92	5
+ 878	8
+ 68	3
+ 484	10
+ 325	9
+ 550	7
+ 444	2
+ 603	5
+ 935	3
+ 522	1
+ 870	9
+ 82	8
+ 163	9
+ 521	5
+ 650	1
+ 794	7
+ 598	7
+ 494	7
+ 974	10
+ 625	3
+ 911	2
+ 951	4
+ 356	6
+ 877	3
+ 842	4
+ 419	7
+ 322	5
+ 476	5
+ 369	10
+ 960	0
+ 143	8
+ 761	7
+ 426	3
+ 408	4
+ 233	0
+ 698	1
+ 209	6
+ 499	6
+ 203	4
+ 856	0
+ 775	3
+ 757	1
+ 776	2
+ 583	1
+ 229	5
+ 164	4
+ 297	9
+ 114	7
+ 180	5
+ 122	4
+ 555	8
+ 556	8
+ 469	1
+ 328	7
+ 431	2
+ 717	2
+ 459	5
+ 302	2
+ 706	9
+ 380	9
+ 428	5
+ 308	7
+ 468	4
+ 447	6
+ 944	6
+ 60	5
+ 390	6
+ 262	9
+ 672	6
+ 531	1
+ 774	2
+ 307	2
+ 721	6
+ 468	4
+ 495	8
+ 363	9
+ 392	7
+ 648	9
+ 93	1
+ 508	0
+ 664	6
+ 536	1
+ 185	8
+ 913	9
+ 390	4
+ 959	2
+ 692	3
+ 397	4
+ 877	9
+ 841	4
+ 713	2
+ 295	1
+ 875	9
+ 965	10
+ 37	5
+ 6	7
+ 42	5
+ 755	2
+ 342	7
+ 84	7
+ 112	0
+ 895	8
+ 310	3
+ 218	2
+ 158	1
+ 559	9
+ 264	9
+ 976	1
+ 796	9
+ 108	8
+ 413	1
+ 533	5
+ 658	3
+ 682	10
+ 956	8
+ 731	1
+ 809	6
+ 873	1
+ 918	1
+ 307	1
+ 152	9
+ 947	4
+ 719	9
+ 555	5
+ 863	7
+ 347	3
+ 778	9
+ 731	4
+ 168	4
+ 435	1
+ 179	2
+ 193	10
+ 791	1
+ 108	7
+ 159	4
+ 786	3
+ 280	7
+ 726	10
+ 655	3
+ 512	5
+ 944	9
+ 793	7
+ 739	5
+ 158	9
+ 937	6
+ 32	1
+ 758	2
+ 104	5
+ 292	2
+ 259	5
+ 626	0
+ 761	9
+ 777	5
+ 903	4
+ 767	4
+ 949	7
+ 274	7
+ 434	0
+ 266	6
+ 921	2
+ 184	10
+ 318	9
+ 178	4
+ 491	5
+ 633	8
+ 921	3
+ 795	7
+ 164	6
+ 168	1
+ 3	9
+ 483	10
+ 647	8
+ 694	1
+ 771	10
+ 673	7
+ 163	9
+ 644	5
+ 799	8
+ 903	3
+ 292	5
+ 40	2
+ 794	8
+ 895	10
+ 407	1
+ 25	4
+ 999	5
+ 362	6
+ 265	1
+ 727	0
+ 16	4
+ 727	2
+ 257	4
+ 660	1
+ 193	6
+ 345	5
+ 98	4
+ 698	9
+ 221	6
+ 850	6
+ 656	9
+ 37	7
+ 383	4
+ 301	6
+ 455	0
+ 684	5
+ 428	4
+ 650	7
+ 781	3
+ 740	10
+ 872	1
+ 459	10
+ 471	2
+ 863	7
+ 749	7
+ 319	4
+ 589	4
+ 59	10
+ 755	4
+ 621	2
+ 388	3
+ 681	8
+ 716	3
+ 501	5
+ 641	2
+ 471	5
+ 327	9
+ 484	8
+ 87	3
+ 490	8
+ 60	8
+ 241	6
+ 166	3
+ 622	9
+ 661	2
+ 132	0
+ 547	8
+ 865	3
+ 144	4
+ 760	8
+ 606	2
+ 299	9
+ 162	8
+ 731	2
+ 130	2
+ 85	2
+ 30	3
+ 840	2
+ 627	5
+ 117	3
+ 705	2
+ 337	3
+ 61	2
+ 515	2
+ 566	3
+ 991	2
+ 506	3
+ 105	7
+ 74	8
+ 916	2
+ 57	0
+ 395	1
+ 328	2
+ 282	10
+ 697	4
+ 243	4
+ 647	6
+ 654	7
+ 781	2
+ 914	3
+ 444	9
+ 520	9
+ 196	6
+ 618	3
+ 461	5
+ 474	5
+ 535	9
+ 604	9
+ 105	9
+ 818	8
+ 285	1
+ 205	9
+ 641	9
+ 641	4
+ 28	6
+ 768	5
+ 461	3
+ 421	7
+ 913	0
+ 927	4
+ 573	4
+ 892	1
+ 271	5
+ 971	4
+ 382	8
+ 179	7
+ 851	4
+ 600	5
+ 243	2
+ 913	3
+ 796	7
+ 742	3
+ 969	2
+ 914	9
+ 202	8
+ 257	8
+ 243	1
+ 882	5
+ 644	9
+ 891	0
+ 643	1
+ 694	5
+ 454	3
+ 986	7
+ 535	9
+ 967	3
+ 580	7
+ 588	5
+ 871	5
+ 432	1
+ 344	7
+ 847	6
+ 837	7
+ 100	5
+ 583	10
+ 508	2
+ 61	2
+ 721	5
+ 497	7
+ 211	0
+ 606	2
+ 363	2
+ 887	10
+ 736	8
+ 454	2
+ 831	8
+ 858	7
+ 384	7
+ 408	5
+ 176	10
+ 475	7
+ 218	5
+ 887	9
+ 51	4
+ 646	3
+ 415	3
+ 440	8
+ 438	3
+ 729	2
+ 86	2
+ 344	9
+ 981	2
+ 596	4
+ 896	0
+ 850	1
+ 995	3
+ 756	2
+ 861	6
+ 152	9
+ 26	8
+ 174	4
+ 50	6
+ 218	5
+ 942	9
+ 663	0
+ 131	0
+ 944	1
+ 208	5
+ 477	1
+ 544	3
+ 176	5
+ 652	9
+ 752	5
+ 574	9
+ 424	6
+ 702	6
+ 41	8
+ 212	3
+ 241	2
+ 207	9
+ 181	3
+ 911	1
+ 450	1
+ 665	9
+ 222	2
+ 254	4
+ 748	9
+ 329	5
+ 418	9
+ 405	8
+ 504	1
+ 441	5
+ 860	7
+ 803	1
+ 807	0
+ 4	10
+ 348	9
+ 114	8
+ 33	8
+ 725	3
+ 988	10
+ 653	7
+ 885	10
+ 238	3
+ 886	6
+ 146	4
+ 751	6
+ 934	6
+ 240	7
+ 712	0
+ 748	7
+ 35	1
+ 631	1
+ 894	7
+ 928	6
+ 920	9
+ 598	6
+ 654	5
+ 556	9
+ 786	4
+ 535	9
+ 832	3
+ 518	8
+ 896	8
+ 504	6
+ 804	3
+ 324	8
+ 347	10
+ 989	2
+ 619	9
+ 860	5
+ 834	5
+ 113	5
+ 942	7
+ 380	7
+ 112	9
+ 659	9
+ 200	2
+ 711	1
+ 934	2
+ 704	7
+ 466	0
+ 578	8
+ 983	6
+ 55	6
+ 485	9
+ 142	3
+ 373	3
+ 808	3
+ 925	2
+ 43	0
+ 102	7
+ 981	3
+ 878	7
+ 398	8
+ 906	1
+ 551	4
+ 129	1
+ 186	1
+ 697	2
+ 715	2
+ 156	9
+ 502	5
+ 112	3
+ 844	0
+ 497	9
+ 74	6
+ 589	1
+ 900	5
+ 747	3
+ 280	7
+ 399	8
+ 26	5
+ 961	2
+ 641	7
+ 453	4
+ 840	6
+ 212	3
+ 138	3
+ 651	10
+ 362	1
+ 869	4
+ 746	5
+ 490	6
+ 925	2
+ 943	2
+ 890	3
+ 36	9
+ 870	10
+ 128	5
+ 655	6
+ 866	5
+ 190	1
+ 837	3
+ 403	5
+ 310	8
+ 636	2
+ 200	4
+ 637	7
+ 28	6
+ 927	10
+ 766	8
+ 313	8
+ 733	2
+ 798	9
+ 695	5
+ 443	6
+ 948	6
+ 640	8
+ 960	0
+ 274	3
+ 808	9
+ 449	0
+ 292	1
+ 698	3
+ 648	6
+ 291	4
+ 443	6
+ 215	2
+ 788	0
+ 37	5
+ 467	5
+ 44	4
+ 112	7
+ 200	1
+ 727	5
+ 342	5
+ 383	8
+ 542	7
+ 877	2
+ 995	5
+ 866	3
+ 938	3
+ 891	2
+ 484	7
+ 167	5
+ 162	6
+ 1	2
+ 48	1
+ 890	2
+ 186	6
+ 721	5
+ 151	1
+ 318	7
+ 779	2
+ 934	8
+ 719	8
+ 61	7
+ 108	10
+ 810	6
+ 632	10
+ 114	8
+ 610	1
+ 0	7
+ 229	9
+ 906	4
+ 506	6
+ 942	7
+ 731	3
+ 350	5
+ 455	3
+ 284	2
+ 83	3
+ 830	2
+ 297	6
+ 783	9
+ 617	9
+ 723	2
+ 12	7
+ 885	2
+ 614	8
+ 656	1
+ 418	6
+ 777	1
+ 858	1
+ 659	3
+ 411	9
+ 486	5
+ 288	3
+ 685	6
+ 957	5
+ 514	6
+ 365	2
+ 801	4
+ 961	7
+ 618	6
+ 477	3
+ 695	9
+ 871	5
+ 44	7
+ 600	7
+ 42	0
+ 646	5
+ 504	9
+ 845	2
+ 520	8
+ 657	0
+ 375	0
+ 272	2
+ 398	2
+ 862	0
+ 809	3
+ 290	5
+ 234	2
+ 976	3
+ 891	6
+ 982	9
+ 587	6
+ 461	1
+ 563	3
+ 280	1
+ 107	9
+ 117	5
+ 958	4
+ 658	4
+ 623	5
+ 372	4
+ 859	7
+ 935	1
+ 823	9
+ 372	7
+ 488	4
+ 646	1
+ 982	1
+ 165	5
+ 412	4
+ 628	5
+ 382	7
+ 2	3
+ 135	7
+ 696	8
+ 179	1
+ 190	0
+ 730	1
+ 131	6
+ 36	5
+ 266	5
+ 857	9
+ 598	8
+ 19	8
+ 384	4
+ 209	0
+ 951	6
+ 759	10
+ 932	9
+ 612	6
+ 652	8
+ 696	8
+ 830	4
+ 966	10
+ 978	0
+ 464	2
+ 527	3
+ 155	1
+ 160	2
+ 889	5
+ 605	1
+ 556	6
+ 690	3
+ 508	6
+ 209	1
+ 249	9
+ 912	9
+ 703	7
+ 370	7
+ 703	3
+ 672	2
+ 591	2
+ 488	7
+ 324	6
+ 921	2
+ 191	5
+ 311	7
+ 82	0
+ 62	6
+ 621	3
+ 710	9
+ 132	6
+ 815	8
+ 364	2
+ 504	1
+ 533	2
+ 234	1
+ 374	7
+ 872	7
+ 369	8
+ 911	6
+ 319	2
+ 307	4
+ 221	4
+ 990	8
+ 641	7
+ 713	8
+ 323	5
+ 608	7
+ 714	1
+ 755	2
+ 288	10
+ 372	7
+ 711	2
+ 360	1
+ 36	3
+ 640	4
+ 492	9
+ 755	7
+ 317	7
+ 556	10
+ 446	3
+ 731	8
+ 798	3
+ 457	5
+ 450	2
+ 760	7
+ 201	1
+ 400	9
+ 375	8
+ 991	4
+ 31	6
+ 765	5
+ 578	5
+ 237	9
+ 265	8
+ 852	7
+ 63	6
+ 481	9
+ 921	9
+ 374	4
+ 149	1
+ 109	3
+ 265	5
+ 261	6
+ 270	3
+ 50	3
+ 883	8
+ 824	5
+ 335	1
+ 355	6
+ 854	2
+ 312	9
+ 789	8
+ 778	7
+ 730	2
+ 81	9
+ 285	2
+ 228	6
+ 700	5
+ 190	10
+ 740	2
+ 271	6
+ 55	1
+ 84	4
+ 156	4
+ 990	0
+ 646	3
+ 927	4
+ 94	7
+ 145	8
+ 856	2
+ 702	1
+ 417	9
+ 692	1
+ 418	9
+ 87	2
+ 122	4
+ 782	2
+ 453	9
+ 567	6
+ 305	6
+ 619	10
+ 859	5
+ 386	10
+ 250	5
+ 776	1
+ 757	5
+ 249	2
+ 407	9
+ 291	8
+ 823	4
+ 983	9
+ 736	8
+ 121	2
+ 631	7
+ 798	9
+ 245	4
+ 886	1
+ 963	3
+ 57	2
+ 803	8
+ 320	6
+ 310	6
+ 734	7
+ 509	0
+ 543	3
+ 403	5
+ 276	1
+ 291	4
+ 328	9
+ 85	1
+ 858	3
+ 544	7
+ 434	5
+ 16	5
+ 719	8
+ 324	0
+ 378	6
+ 607	1
+ 352	1
+ 137	9
+ 447	5
+ 420	7
+ 679	7
+ 119	0
+ 634	2
+ 134	5
+ 535	7
+ 236	10
+ 184	3
+ 461	9
+ 71	8
+ 942	4
+ 418	5
+ 561	8
+ 664	7
+ 664	1
+ 238	1
+ 834	9
+ 796	10
+ 924	4
+ 158	1
+ 921	7
+ 735	2
+ 662	9
+ 409	1
+ 822	5
+ 907	8
+ 929	3
+ 312	5
+ 95	10
+ 188	8
+ 87	4
+ 844	9
+ 342	6
+ 874	3
+ 69	0
+ 324	10
+ 724	1
+ 148	4
+ 977	6
+ 510	8
+ 38	4
+ 563	10
+ 743	9
+ 458	8
+ 851	6
+ 598	9
+ 72	4
+ 859	4
+ 81	7
+ 680	2
+ 764	0
+ 141	5
+ 63	3
+ 875	0
+ 846	4
+ 839	9
+ 801	4
+ 851	5
+ 277	3
+ 382	1
+ 955	10
+ 65	0
+ 421	9
+ 441	5
+ 656	1
+ 653	4
+ 125	8
+ 908	2
+ 83	8
+ 228	9
+ 167	1
+ 813	10
+ 469	7
+ 513	7
+ 974	9
+ 873	9
+ 875	9
+ 955	3
+ 862	4
+ 799	5
+ 517	5
+ 939	6
+ 245	8
+ 830	3
+ 630	1
+ 257	8
+ 126	1
+ 765	6
+ 734	3
+ 341	7
+ 173	2
+ 637	0
+ 152	6
+ 344	0
+ 988	1
+ 533	5
+ 594	5
+ 148	8
+ 319	10
+ 166	9
+ 37	4
+ 745	2
+ 493	5
+ 757	2
+ 788	1
+ 935	10
+ 311	6
+ 9	5
+ 164	4
+ 478	2
+ 495	0
+ 658	1
+ 483	8
+ 927	8
+ 785	1
+ 751	8
+ 516	5
+ 984	0
+ 7	7
+ 235	8
+ 840	2
+ 756	2
+ 742	8
+ 615	9
+ 118	1
+ 58	6
+ 104	7
+ 700	6
+ 522	6
+ 389	3
+ 720	1
+ 128	2
+ 637	1
+ 244	6
+ 854	5
+ 439	7
+ 650	2
+ 845	4
+ 961	5
+ 298	1
+ 552	4
+ 690	7
+ 72	4
+ 243	6
+ 18	6
+ 901	7
+ 772	0
+ 973	4
+ 142	2
+ 52	10
+ 695	5
+ 691	3
+ 687	5
+ 737	6
+ 995	0
+ 725	5
+ 392	4
+ 203	5
+ 806	4
+ 59	8
+ 77	10
+ 562	8
+ 989	5
+ 258	1
+ 751	3
+ 127	4
+ 802	8
+ 792	5
+ 353	5
+ 136	3
+ 564	9
+ 895	10
+ 278	1
+ 420	1
+ 544	5
+ 908	6
+ 438	5
+ 471	4
+ 5	7
+ 558	8
+ 40	7
+ 203	8
+ 503	10
+ 331	9
+ 523	5
+ 205	1
+ 330	1
+ 42	6
+ 199	5
+ 692	7
+ 941	6
+ 363	4
+ 70	8
+ 806	1
+ 563	4
+ 831	6
+ 49	0
+ 445	6
+ 28	8
+ 408	6
+ 245	6
+ 638	6
+ 713	7
+ 182	9
+ 142	9
+ 654	1
+ 473	0
+ 463	5
+ 852	3
+ 619	4
+ 632	4
+ 18	7
+ 484	5
+ 233	5
+ 240	6
+ 63	5
+ 254	7
+ 59	10
+ 380	2
+ 880	5
+ 114	5
+ 606	6
+ 551	1
+ 131	4
+ 337	7
+ 818	10
+ 199	8
+ 650	7
+ 299	9
+ 195	5
+ 524	3
+ 23	8
+ 958	1
+ 746	3
+ 322	6
+ 860	4
+ 159	5
+ 23	7
+ 535	2
+ 114	9
+ 904	9
+ 842	1
+ 767	5
+ 786	1
+ 375	10
+ 604	9
+ 239	6
+ 678	2
+ 708	4
+ 534	0
+ 49	4
+ 466	2
+ 861	5
+ 919	4
+ 643	0
+ 269	5
+ 964	1
+ 650	7
+ 603	4
+ 797	10
+ 418	4
+ 877	7
+ 28	6
+ 853	7
+ 979	4
+ 766	0
+ 782	2
+ 236	6
+ 721	2
+ 40	4
+ 188	3
+ 911	2
+ 419	6
+ 884	0
+ 999	7
+ 1000	4
+ 82	9
+ 73	1
+ 432	9
+ 846	4
+ 313	6
+ 439	1
+ 844	7
+ 739	6
+ 831	8
+ 929	0
+ 87	8
+ 172	5
+ 402	1
+ 528	4
+ 736	5
+ 817	8
+ 405	9
+ 927	8
+ 817	8
+ 249	1
+ 384	7
+ 225	2
+ 364	10
+ 793	2
+ 742	7
+ 215	8
+ 562	4
+ 336	10
+ 443	9
+ 365	2
+ 392	2
+ 997	8
+ 72	9
+ 635	9
+ 697	9
+ 19	1
+ 573	2
+ 309	9
+ 208	1
+ 132	10
+ 824	3
+ 780	4
+ 734	1
+ 351	2
+ 980	7
+ 356	4
+ 897	4
+ 170	10
+ 276	8
+ 858	10
+ 690	9
+ 54	3
+ 121	4
+ 199	3
+ 466	3
+ 280	3
+ 678	1
+ 677	4
+ 175	0
+ 589	2
+ 743	9
+ 527	6
+ 297	7
+ 610	6
+ 502	5
+ 547	2
+ 345	6
+ 454	5
+ 965	7
+ 795	4
+ 983	1
+ 721	7
+ 135	4
+ 74	3
+ 425	7
+ 465	2
+ 607	10
+ 808	9
+ 689	4
+ 478	2
+ 886	0
+ 382	2
+ 626	8
+ 697	6
+ 488	5
+ 21	5
+ 567	7
+ 133	7
+ 140	2
+ 12	6
+ 869	5
+ 734	5
+ 469	5
+ 381	2
+ 960	9
+ 349	8
+ 884	7
+ 77	5
+ 567	8
+ 100	1
+ 266	1
+ 527	8
+ 864	7
+ 535	0
+ 867	5
+ 570	7
+ 24	3
+ 213	5
+ 845	6
+ 651	8
+ 453	0
+ 651	3
+ 732	7
+ 846	3
+ 501	9
+ 355	8
+ 67	9
+ 600	9
+ 542	1
+ 935	4
+ 682	5
+ 146	7
+ 808	4
+ 199	7
+ 953	9
+ 459	4
+ 851	1
+ 743	6
+ 837	6
+ 882	3
+ 534	2
+ 105	6
+ 118	7
+ 532	7
+ 840	5
+ 70	5
+ 971	2
+ 228	8
+ 575	4
+ 433	5
+ 277	9
+ 935	1
+ 1	7
+ 710	8
+ 266	6
+ 176	8
+ 828	3
+ 402	9
+ 986	9
+ 607	8
+ 399	7
+ 348	4
+ 892	6
+ 150	5
+ 1	6
+ 996	3
+ 474	9
+ 406	5
+ 609	1
+ 312	9
+ 708	5
+ 676	5
+ 768	1
+ 483	8
+ 10	1
+ 580	4
+ 766	9
+ 780	7
+ 502	9
+ 125	5
+ 513	1
+ 782	10
+ 52	2
+ 461	7
+ 304	8
+ 535	0
+ 261	2
+ 548	0
+ 288	0
+ 783	3
+ 121	4
+ 708	9
+ 290	5
+ 545	8
+ 418	7
+ 296	9
+ 791	1
+ 918	8
+ 266	4
+ 504	6
+ 153	0
+ 581	4
+ 250	1
+ 443	5
+ 161	2
+ 836	3
+ 589	5
+ 169	9
+ 32	7
+ 671	4
+ 384	10
+ 381	2
+ 45	3
+ 19	3
+ 678	5
+ 881	8
+ 561	5
+ 244	8
+ 591	7
+ 349	8
+ 913	2
+ 34	5
+ 728	2
+ 380	8
+ 916	1
+ 209	3
+ 18	6
+ 476	1
+ 890	5
+ 374	6
+ 17	3
+ 399	6
+ 716	6
+ 389	3
+ 330	7
+ 60	2
+ 922	1
+ 744	6
+ 296	1
+ 409	2
+ 174	6
+ 513	2
+ 209	10
+ 255	1
+ 483	6
+ 667	5
+ 883	1
+ 78	6
+ 708	5
+ 907	0
+ 204	10
+ 280	1
+ 60	0
+ 775	4
+ 147	2
+ 569	3
+ 803	1
+ 512	0
+ 71	8
+ 111	6
+ 396	8
+ 53	3
+ 842	1
+ 878	6
+ 597	8
+ 588	8
+ 751	9
+ 927	8
+ 891	7
+ 169	0
+ 886	7
+ 359	7
+ 820	9
+ 701	9
+ 638	8
+ 445	0
+ 588	5
+ 312	4
+ 628	2
+ 981	2
+ 975	6
+ 26	7
+ 437	10
+ 538	3
+ 655	7
+ 366	5
+ 445	7
+ 229	3
+ 595	9
+ 156	2
+ 741	6
+ 266	3
+ 98	6
+ 760	7
+ 768	7
+ 952	7
+ 310	10
+ 469	7
+ 931	0
+ 74	6
+ 713	4
+ 127	2
+ 164	4
+ 423	8
+ 286	6
+ 992	0
+ 180	3
+ 357	3
+ 837	1
+ 5	6
+ 858	10
+ 348	2
+ 935	8
+ 915	9
+ 823	10
+ 452	5
+ 429	6
+ 694	6
+ 935	1
+ 352	2
+ 696	3
+ 249	9
+ 602	6
+ 153	4
+ 722	2
+ 44	6
+ 115	4
+ 748	0
+ 208	7
+ 915	0
+ 652	4
+ 566	1
+ 946	3
+ 673	9
+ 377	0
+ 102	1
+ 369	4
+ 948	10
+ 956	1
+ 409	7
+ 259	5
+ 258	4
+ 844	0
+ 423	1
+ 669	3
+ 82	3
+ 705	6
+ 402	7
+ 908	1
+ 533	3
+ 101	6
+ 357	5
+ 986	3
+ 440	9
+ 406	8
+ 620	7
+ 303	9
+ 39	1
+ 885	5
+ 199	6
+ 801	3
+ 875	5
+ 929	3
+ 157	8
+ 353	7
+ 123	5
+ 325	5
+ 923	3
+ 785	4
+ 252	2
+ 213	9
+ 857	5
+ 751	9
+ 663	6
+ 359	9
+ 190	2
+ 142	1
+ 665	1
+ 343	8
+ 909	7
+ 513	0
+ 149	8
+ 513	1
+ 148	3
+ 435	4
+ 489	6
+ 273	3
+ 163	0
+ 243	8
+ 660	6
+ 687	8
+ 761	8
+ 914	4
+ 901	3
+ 249	8
+ 952	8
+ 843	1
+ 600	4
+ 173	7
+ 653	6
+ 149	1
+ 255	4
+ 489	4
+ 446	7
+ 244	1
+ 334	9
+ 955	1
+ 760	9
+ 521	7
+ 126	8
+ 471	1
+ 532	3
+ 180	1
+ 668	4
+ 880	3
+ 961	0
+ 464	2
+ 450	10
+ 634	9
+ 685	9
+ 2	0
+ 809	10
+ 113	6
+ 826	6
+ 230	10
+ 405	7
+ 30	9
+ 14	2
+ 69	7
+ 563	9
+ 3	5
+ 978	5
+ 740	4
+ 420	4
+ 324	1
+ 252	3
+ 123	1
+ 283	2
+ 631	1
+ 871	9
+ 60	3
+ 561	1
+ 213	6
+ 301	3
+ 257	9
+ 232	3
+ 388	2
+ 727	1
+ 637	1
+ 501	10
+ 252	8
+ 288	4
+ 815	6
+ 612	4
+ 678	5
+ 306	7
+ 759	9
+ 829	10
+ 442	1
+ 255	7
+ 994	5
+ 959	4
+ 696	7
+ 509	3
+ 833	0
+ 294	1
+ 764	6
+ 461	6
+ 152	1
+ 25	8
+ 555	3
+ 569	3
+ 199	4
+ 287	6
+ 528	5
+ 339	5
+ 28	3
+ 903	7
+ 983	4
+ 57	8
+ 422	4
+ 902	2
+ 933	4
+ 765	1
+ 435	8
+ 915	10
+ 122	5
+ 304	3
+ 882	6
+ 961	4
+ 133	3
+ 931	2
+ 598	8
+ 885	6
+ 246	9
+ 397	7
+ 292	3
+ 853	2
+ 662	6
+ 310	1
+ 409	2
+ 86	5
+ 709	4
+ 852	6
+ 982	8
+ 1	1
+ 114	9
+ 276	7
+ 766	2
+ 293	0
+ 102	7
+ 680	4
+ 989	5
+ 620	7
+ 152	9
+ 747	6
+ 154	8
+ 92	9
+ 224	9
+ 454	2
+ 758	5
+ 321	9
+ 386	6
+ 584	2
+ 758	9
+ 164	9
+ 567	8
+ 255	6
+ 377	9
+ 207	5
+ 804	10
+ 89	10
+ 788	2
+ 821	0
+ 126	3
+ 218	9
+ 729	5
+ 757	1
+ 136	3
+ 267	9
+ 219	4
+ 755	8
+ 275	0
+ 342	7
+ 885	5
+ 179	7
+ 503	3
+ 648	3
+ 450	5
+ 303	6
+ 743	5
+ 460	5
+ 60	2
+ 587	2
+ 559	9
+ 91	8
+ 285	8
+ 563	6
+ 856	9
+ 211	7
+ 454	4
+ 430	10
+ 659	1
+ 249	1
+ 546	6
+ 685	3
+ 72	1
+ 762	1
+ 363	3
+ 328	9
+ 202	4
+ 699	5
+ 265	3
+ 47	1
+ 168	3
+ 862	6
+ 649	3
+ 580	3
+ 369	8
+ 417	9
+ 379	1
+ 205	5
+ 247	10
+ 583	6
+ 315	9
+ 532	5
+ 331	2
+ 5	6
+ 493	1
+ 717	7
+ 310	6
+ 283	10
+ 870	9
+ 267	2
+ 691	7
+ 154	1
+ 786	4
+ 522	0
+ 326	1
+ 642	6
+ 17	2
+ 158	3
+ 405	2
+ 943	9
+ 215	7
+ 559	5
+ 238	8
+ 484	1
+ 704	8
+ 346	4
+ 435	5
+ 465	2
+ 860	10
+ 253	2
+ 92	9
+ 826	1
+ 70	10
+ 456	5
+ 147	4
+ 373	4
+ 60	9
+ 887	3
+ 774	4
+ 405	5
+ 122	8
+ 873	6
+ 253	3
+ 778	1
+ 326	0
+ 298	4
+ 927	1
+ 527	10
+ 109	10
+ 471	3
+ 383	8
+ 618	4
+ 775	5
+ 740	5
+ 875	1
+ 27	10
+ 897	9
+ 554	1
+ 239	3
+ 263	6
+ 362	6
+ 982	3
+ 686	5
+ 285	8
+ 492	8
+ 51	9
+ 600	7
+ 317	4
+ 173	1
+ 924	0
+ 203	10
+ 45	1
+ 851	6
+ 250	1
+ 930	5
+ 654	3
+ 74	6
+ 581	8
+ 145	9
+ 554	6
+ 623	6
+ 511	2
+ 274	8
+ 598	4
+ 886	5
+ 496	1
+ 474	5
+ 189	3
+ 141	4
+ 414	1
+ 953	1
+ 363	0
+ 704	9
+ 786	8
+ 811	3
+ 485	4
+ 946	10
+ 657	2
+ 825	3
+ 668	7
+ 778	2
+ 800	3
+ 705	10
+ 576	8
+ 429	10
+ 916	4
+ 58	3
+ 409	8
+ 225	2
+ 610	0
+ 536	1
+ 470	5
+ 92	1
+ 702	9
+ 383	4
+ 628	2
+ 533	4
+ 412	2
+ 417	10
+ 84	8
+ 978	0
+ 229	0
+ 280	6
+ 798	5
+ 834	4
+ 540	4
+ 504	0
+ 852	6
+ 138	6
+ 512	5
+ 925	1
+ 682	5
+ 567	1
+ 696	10
+ 82	8
+ 830	1
+ 780	1
+ 96	1
+ 697	9
+ 565	5
+ 302	1
+ 900	8
+ 116	8
+ 401	3
+ 307	9
+ 774	2
+ 52	5
+ 690	6
+ 550	4
+ 603	6
+ 166	4
+ 691	9
+ 493	8
+ 7	2
+ 681	6
+ 720	10
+ 677	6
+ 789	8
+ 374	2
+ 46	7
+ 103	8
+ 913	2
+ 276	6
+ 774	8
+ 989	4
+ 457	2
+ 811	1
+ 102	3
+ 935	1
+ 493	6
+ 680	2
+ 601	4
+ 835	4
+ 149	2
+ 580	2
+ 889	7
+ 14	8
+ 838	3
+ 404	6
+ 115	4
+ 989	6
+ 548	8
+ 720	6
+ 103	7
+ 758	6
+ 272	4
+ 810	9
+ 795	6
+ 262	9
+ 852	8
+ 138	7
+ 525	2
+ 543	4
+ 442	9
+ 975	6
+ 340	10
+ 129	9
+ 764	8
+ 537	9
+ 504	3
+ 463	8
+ 733	3
+ 649	5
+ 916	9
+ 471	8
+ 754	6
+ 510	3
+ 761	1
+ 642	2
+ 1000	6
+ 761	3
+ 581	9
+ 227	3
+ 740	8
+ 211	2
+ 58	7
+ 21	8
+ 946	7
+ 318	9
+ 582	8
+ 631	3
+ 398	1
+ 615	2
+ 194	3
+ 363	2
+ 875	1
+ 533	5
+ 16	8
+ 801	8
+ 522	0
+ 0	6
+ 686	0
+ 371	6
+ 692	7
+ 494	3
+ 478	1
+ 610	9
+ 266	2
+ 36	5
+ 483	4
+ 653	4
+ 524	2
+ 814	5
+ 945	6
+ 296	5
+ 627	3
+ 47	3
+ 318	4
+ 944	0
+ 108	4
+ 283	6
+ 564	9
+ 463	8
+ 118	5
+ 290	6
+ 898	9
+ 959	4
+ 129	8
+ 963	1
+ 388	3
+ 541	0
+ 555	6
+ 327	9
+ 6	3
+ 882	1
+ 711	2
+ 700	3
+ 58	2
+ 105	2
+ 662	4
+ 777	6
+ 338	7
+ 983	5
+ 509	9
+ 540	9
+ 205	1
+ 912	8
+ 669	2
+ 633	7
+ 511	5
+ 790	2
+ 680	5
+ 496	7
+ 653	6
+ 915	3
+ 995	7
+ 875	3
+ 429	9
+ 800	9
+ 804	3
+ 835	0
+ 422	7
+ 768	1
+ 987	4
+ 767	5
+ 915	6
+ 720	6
+ 47	2
+ 334	7
+ 817	2
+ 15	8
+ 941	9
+ 145	4
+ 747	9
+ 307	6
+ 286	1
+ 559	7
+ 890	3
+ 798	9
+ 727	6
+ 375	6
+ 122	1
+ 238	2
+ 311	6
+ 869	1
+ 820	9
+ 941	8
+ 773	1
+ 130	5
+ 31	4
+ 70	3
+ 580	6
+ 24	5
+ 956	8
+ 347	7
+ 387	7
+ 325	5
+ 817	6
+ 678	1
+ 134	5
+ 257	10
+ 431	2
+ 715	2
+ 284	8
+ 724	3
+ 281	8
+ 632	9
+ 423	7
+ 331	4
+ 477	7
+ 62	9
+ 400	4
+ 374	2
+ 950	1
+ 346	1
+ 599	6
+ 38	0
+ 800	8
+ 234	1
+ 597	10
+ 399	9
+ 751	0
+ 740	2
+ 686	1
+ 554	2
+ 749	6
+ 28	1
+ 2	4
+ 366	10
+ 454	7
+ 36	1
+ 314	1
+ 83	1
+ 827	3
+ 198	4
+ 275	6
+ 304	0
+ 628	0
+ 201	3
+ 114	8
+ 477	9
+ 370	5
+ 12	4
+ 907	4
+ 324	4
+ 89	4
+ 414	4
+ 435	5
+ 517	3
+ 815	7
+ 687	1
+ 313	10
+ 116	9
+ 34	3
+ 255	1
+ 71	7
+ 12	4
+ 237	0
+ 812	1
+ 401	1
+ 505	5
+ 496	9
+ 893	9
+ 417	4
+ 193	2
+ 125	9
+ 321	4
+ 871	4
+ 380	9
+ 753	6
+ 53	8
+ 366	1
+ 264	6
+ 88	1
+ 747	5
+ 213	3
+ 979	7
+ 171	9
+ 640	6
+ 281	8
+ 819	4
+ 714	1
+ 845	6
+ 577	2
+ 489	3
+ 859	5
+ 154	2
+ 607	4
+ 828	7
+ 495	6
+ 184	7
+ 827	2
+ 417	10
+ 34	1
+ 586	3
+ 890	4
+ 721	6
+ 545	6
+ 188	1
+ 791	7
+ 452	7
+ 219	6
+ 875	8
+ 25	7
+ 521	5
+ 279	7
+ 228	1
+ 868	6
+ 105	9
+ 701	7
+ 217	6
+ 96	9
+ 196	6
+ 505	4
+ 763	3
+ 61	2
+ 946	3
+ 823	8
+ 107	8
+ 525	6
+ 368	8
+ 333	6
+ 910	2
+ 240	0
+ 103	9
+ 706	3
+ 533	8
+ 258	7
+ 443	8
+ 112	2
+ 58	2
+ 423	0
+ 455	2
+ 825	6
+ 93	4
+ 190	5
+ 154	5
+ 56	1
+ 725	3
+ 79	8
+ 237	8
+ 147	8
+ 587	4
+ 498	0
+ 167	6
+ 236	2
+ 785	7
+ 230	2
+ 904	1
+ 801	10
+ 405	10
+ 458	6
+ 515	5
+ 623	2
+ 810	7
+ 67	0
+ 486	2
+ 817	1
+ 619	3
+ 102	8
+ 926	3
+ 11	7
+ 998	2
+ 950	9
+ 297	8
+ 899	7
+ 743	4
+ 261	3
+ 871	9
+ 498	7
+ 585	6
+ 728	1
+ 779	5
+ 144	4
+ 861	2
+ 183	8
+ 585	2
+ 498	6
+ 436	4
+ 485	7
+ 200	4
+ 434	9
+ 741	7
+ 202	6
+ 578	7
+ 293	2
+ 264	0
+ 234	0
+ 566	4
+ 440	4
+ 624	6
+ 213	2
+ 817	7
+ 791	3
+ 160	3
+ 984	4
+ 660	4
+ 303	4
+ 113	5
+ 13	7
+ 204	3
+ 855	5
+ 326	1
+ 511	9
+ 467	10
+ 318	1
+ 573	5
+ 300	4
+ 242	1
+ 642	4
+ 367	6
+ 762	0
+ 45	1
+ 428	2
+ 570	4
+ 851	8
+ 746	7
+ 243	1
+ 795	8
+ 963	3
+ 705	3
+ 354	3
+ 811	7
+ 668	1
+ 744	3
+ 456	1
+ 937	2
+ 137	10
+ 283	6
+ 140	9
+ 5	10
+ 628	8
+ 697	9
+ 823	5
+ 626	8
+ 755	3
+ 66	1
+ 609	9
+ 762	3
+ 931	5
+ 586	4
+ 616	5
+ 604	8
+ 505	9
+ 318	6
+ 741	3
+ 636	4
+ 74	3
+ 241	9
+ 825	9
+ 683	6
+ 197	7
+ 688	8
+ 626	5
+ 82	6
+ 956	7
+ 944	6
+ 192	5
+ 325	7
+ 436	6
+ 342	2
+ 965	10
+ 547	0
+ 312	8
+ 936	1
+ 654	6
+ 717	9
+ 368	4
+ 658	10
+ 855	7
+ 551	8
+ 409	5
+ 382	6
+ 44	7
+ 298	5
+ 349	6
+ 658	3
+ 619	2
+ 354	9
+ 992	3
+ 67	6
+ 909	8
+ 498	3
+ 188	2
+ 271	0
+ 895	8
+ 854	3
+ 318	2
+ 905	4
+ 943	2
+ 843	3
+ 843	5
+ 607	5
+ 705	10
+ 392	7
+ 251	5
+ 343	2
+ 242	8
+ 437	4
+ 995	7
+ 474	9
+ 530	3
+ 195	8
+ 565	1
+ 210	5
+ 303	1
+ 800	1
+ 553	4
+ 609	3
+ 368	0
+ 955	6
+ 460	3
+ 780	7
+ 138	2
+ 133	1
+ 926	6
+ 24	5
+ 935	2
+ 304	5
+ 318	5
+ 8	6
+ 568	8
+ 768	1
+ 216	4
+ 379	6
+ 377	3
+ 204	8
+ 631	10
+ 539	8
+ 202	7
+ 901	1
+ 279	9
+ 584	2
+ 143	9
+ 714	5
+ 403	7
+ 83	10
+ 530	9
+ 92	7
+ 228	5
+ 331	6
+ 804	5
+ 442	4
+ 520	10
+ 203	7
+ 652	1
+ 850	9
+ 29	4
+ 145	2
+ 323	9
+ 633	7
+ 581	7
+ 696	1
+ 567	8
+ 857	8
+ 259	2
+ 400	1
+ 723	8
+ 498	2
+ 822	7
+ 965	5
+ 804	8
+ 405	8
+ 249	6
+ 5	6
+ 409	6
+ 297	10
+ 354	10
+ 100	9
+ 782	10
+ 716	0
+ 146	1
+ 104	9
+ 958	6
+ 112	8
+ 302	1
+ 255	1
+ 892	7
+ 939	1
+ 211	9
+ 713	6
+ 582	0
+ 609	9
+ 4	7
+ 857	8
+ 667	6
+ 828	8
+ 690	9
+ 683	6
+ 533	8
+ 428	8
+ 873	7
+ 942	8
+ 344	9
+ 907	6
+ 825	6
+ 174	4
+ 630	8
+ 343	6
+ 492	2
+ 421	2
+ 774	2
+ 972	5
+ 180	7
+ 112	7
+ 450	5
+ 549	3
+ 224	5
+ 88	6
+ 372	10
+ 122	2
+ 614	3
+ 604	2
+ 77	9
+ 880	6
+ 148	3
+ 728	9
+ 550	7
+ 386	7
+ 354	5
+ 444	8
+ 38	10
+ 127	3
+ 484	2
+ 829	9
+ 209	10
+ 53	8
+ 245	7
+ 68	3
+ 605	9
+ 892	8
+ 249	6
+ 674	8
+ 319	1
+ 529	7
+ 558	10
+ 478	6
+ 967	6
+ 858	5
+ 820	7
+ 307	0
+ 637	4
+ 852	9
+ 19	9
+ 205	6
+ 869	1
+ 376	1
+ 717	1
+ 917	0
+ 111	4
+ 709	7
+ 420	2
+ 265	4
+ 792	1
+ 838	6
+ 809	1
+ 640	4
+ 506	5
+ 328	5
+ 414	5
+ 148	3
+ 630	5
+ 400	3
+ 576	3
+ 382	7
+ 764	1
+ 356	2
+ 279	6
+ 571	1
+ 743	4
+ 683	6
+ 554	3
+ 998	1
+ 816	3
+ 585	2
+ 858	7
+ 512	5
+ 258	9
+ 835	8
+ 230	2
+ 520	10
+ 308	9
+ 177	6
+ 497	7
+ 659	2
+ 157	3
+ 793	7
+ 665	8
+ 772	5
+ 116	4
+ 711	10
+ 90	2
+ 463	3
+ 136	3
+ 181	4
+ 514	7
+ 359	8
+ 577	5
+ 410	1
+ 285	1
+ 314	4
+ 411	1
+ 153	1
+ 897	9
+ 557	0
+ 281	3
+ 988	4
+ 492	5
+ 719	6
+ 748	9
+ 993	3
+ 601	4
+ 85	2
+ 889	5
+ 251	2
+ 564	6
+ 616	10
+ 672	8
+ 50	6
+ 694	6
+ 582	10
+ 875	6
+ 346	4
+ 21	1
+ 994	8
+ 964	10
+ 31	6
+ 340	1
+ 742	2
+ 610	10
+ 402	2
+ 559	0
+ 148	2
+ 786	2
+ 800	5
+ 805	4
+ 455	7
+ 952	8
+ 48	10
+ 866	0
+ 741	8
+ 29	8
+ 395	4
+ 887	1
+ 597	5
+ 132	10
+ 670	7
+ 17	8
+ 921	8
+ 17	7
+ 283	8
+ 103	7
+ 503	1
+ 541	6
+ 27	4
+ 592	8
+ 238	6
+ 539	6
+ 990	4
+ 771	6
+ 922	9
+ 586	6
+ 593	6
+ 411	5
+ 406	4
+ 235	7
+ 250	3
+ 428	8
+ 393	10
+ 303	4
+ 376	9
+ 188	6
+ 516	7
+ 246	5
+ 153	0
+ 93	1
+ 919	7
+ 667	5
+ 282	1
+ 27	7
+ 506	3
+ 378	8
+ 600	8
+ 509	10
+ 775	8
+ 414	2
+ 707	6
+ 765	2
+ 328	0
+ 729	5
+ 28	8
+ 556	9
+ 501	2
+ 460	8
+ 301	5
+ 471	8
+ 749	8
+ 563	3
+ 655	1
+ 342	4
+ 883	8
+ 582	6
+ 357	3
+ 813	7
+ 357	5
+ 166	4
+ 364	7
+ 333	9
+ 945	8
+ 649	2
+ 280	1
+ 53	0
+ 969	6
+ 377	6
+ 688	7
+ 55	6
+ 476	6
+ 161	8
+ 983	10
+ 519	3
+ 516	7
+ 726	9
+ 407	1
+ 745	4
+ 853	4
+ 598	1
+ 514	7
+ 161	5
+ 268	5
+ 107	10
+ 258	2
+ 527	7
+ 799	7
+ 567	8
+ 663	1
+ 123	2
+ 772	8
+ 59	2
+ 909	8
+ 532	8
+ 197	1
+ 894	7
+ 781	1
+ 193	0
+ 593	3
+ 5	9
+ 463	5
+ 585	3
+ 221	2
+ 45	9
+ 238	2
+ 63	0
+ 18	1
+ 189	9
+ 925	7
+ 688	1
+ 851	6
+ 833	6
+ 636	0
+ 681	2
+ 327	7
+ 80	8
+ 217	7
+ 53	4
+ 817	1
+ 322	1
+ 266	4
+ 66	3
+ 506	3
+ 210	4
+ 976	9
+ 554	8
+ 480	4
+ 458	1
+ 414	1
+ 345	7
+ 824	4
+ 532	0
+ 90	6
+ 480	9
+ 683	8
+ 963	9
+ 187	0
+ 234	7
+ 284	4
+ 124	3
+ 342	7
+ 87	8
+ 66	5
+ 938	5
+ 683	3
+ 221	5
+ 708	8
+ 548	8
+ 338	0
+ 706	0
+ 830	7
+ 971	0
+ 699	2
+ 709	10
+ 649	8
+ 244	10
+ 511	3
+ 813	6
+ 875	8
+ 57	6
+ 34	3
+ 66	7
+ 30	6
+ 541	4
+ 642	2
+ 390	5
+ 917	4
+ 487	6
+ 565	2
+ 600	2
+ 29	8
+ 205	5
+ 174	0
+ 118	0
+ 769	2
+ 608	8
+ 452	7
+ 545	5
+ 288	1
+ 851	9
+ 333	2
+ 401	3
+ 601	9
+ 867	2
+ 84	5
+ 380	1
+ 311	6
+ 654	5
+ 604	8
+ 535	4
+ 947	1
+ 176	4
+ 817	7
+ 881	1
+ 808	7
+ 34	1
+ 972	4
+ 392	6
+ 322	3
+ 740	4
+ 725	1
+ 520	0
+ 706	2
+ 521	3
+ 947	1
+ 683	9
+ 199	9
+ 292	0
+ 581	2
+ 120	4
+ 905	2
+ 529	9
+ 588	9
+ 450	9
+ 179	2
+ 318	9
+ 310	8
+ 941	0
+ 13	5
+ 325	10
+ 518	0
+ 853	7
+ 867	1
+ 732	4
+ 318	9
+ 836	2
+ 6	4
+ 100	6
+ 287	6
+ 505	5
+ 741	8
+ 370	1
+ 661	3
+ 67	7
+ 773	4
+ 633	3
+ 401	5
+ 7	3
+ 631	7
+ 716	9
+ 592	6
+ 173	6
+ 917	3
+ 192	2
+ 824	7
+ 670	6
+ 520	0
+ 616	2
+ 351	7
+ 854	1
+ 75	5
+ 414	5
+ 973	4
+ 744	6
+ 160	5
+ 554	8
+ 11	7
+ 349	9
+ 0	5
+ 132	8
+ 239	8
+ 389	8
+ 842	0
+ 941	2
+ 688	8
+ 317	8
+ 282	7
+ 239	3
+ 151	10
+ 860	3
+ 441	4
+ 62	5
+ 141	4
+ 381	1
+ 952	5
+ 965	2
+ 315	4
+ 950	2
+ 359	9
+ 351	0
+ 686	7
+ 809	10
+ 397	0
+ 224	5
+ 30	1
+ 859	5
+ 497	9
+ 924	6
+ 331	3
+ 779	3
+ 818	7
+ 474	1
+ 99	4
+ 291	5
+ 316	6
+ 504	0
+ 308	3
+ 970	7
+ 361	2
+ 254	4
+ 277	1
+ 863	8
+ 33	8
+ 413	4
+ 93	2
+ 648	9
+ 937	1
+ 44	0
+ 546	3
+ 493	9
+ 976	10
+ 863	3
+ 310	8
+ 991	7
+ 27	2
+ 63	3
+ 358	9
+ 78	4
+ 714	5
+ 755	8
+ 682	4
+ 717	6
+ 525	8
+ 654	1
+ 97	1
+ 933	1
+ 144	8
+ 358	5
+ 629	3
+ 126	7
+ 593	2
+ 959	10
+ 115	0
+ 342	8
+ 527	1
+ 635	2
+ 501	4
+ 828	0
+ 114	5
+ 96	2
+ 630	0
+ 284	8
+ 825	6
+ 228	5
+ 990	4
+ 109	6
+ 542	1
+ 534	7
+ 105	9
+ 485	6
+ 974	1
+ 842	5
+ 473	7
+ 500	6
+ 152	6
+ 798	8
+ 625	1
+ 555	4
+ 723	8
+ 903	7
+ 136	0
+ 296	7
+ 80	8
+ 334	2
+ 706	8
+ 818	7
+ 940	7
+ 154	4
+ 329	7
+ 1000	5
+ 249	8
+ 264	9
+ 879	8
+ 323	6
+ 602	2
+ 314	7
+ 239	6
+ 416	3
+ 439	7
+ 505	1
+ 569	3
+ 825	5
+ 982	10
+ 921	3
+ 633	9
+ 793	9
+ 718	1
+ 756	6
+ 877	1
+ 198	5
+ 306	5
+ 217	5
+ 121	6
+ 864	6
+ 382	4
+ 706	10
+ 691	5
+ 460	7
+ 511	4
+ 985	1
+ 302	8
+ 26	0
+ 835	8
+ 617	7
+ 862	8
+ 191	2
+ 326	4
+ 713	4
+ 41	6
+ 8	4
+ 946	7
+ 375	6
+ 245	8
+ 310	8
+ 216	3
+ 900	5
+ 73	9
+ 538	9
+ 708	2
+ 620	6
+ 970	8
+ 738	3
+ 219	5
+ 743	3
+ 28	8
+ 683	10
+ 465	1
+ 611	7
+ 893	9
+ 466	1
+ 215	4
+ 626	3
+ 291	2
+ 196	10
+ 319	8
+ 569	3
+ 627	3
+ 585	8
+ 758	3
+ 107	8
+ 80	8
+ 759	5
+ 848	4
+ 255	7
+ 291	7
+ 849	5
+ 87	5
+ 794	4
+ 640	10
+ 378	10
+ 807	9
+ 249	4
+ 253	8
+ 281	0
+ 162	4
+ 797	2
+ 177	6
+ 787	0
+ 926	0
+ 766	2
+ 763	6
+ 723	9
+ 92	5
+ 228	7
+ 507	6
+ 692	3
+ 552	9
+ 748	8
+ 775	0
+ 817	9
+ 417	6
+ 179	6
+ 170	10
+ 619	1
+ 7	4
+ 312	8
+ 1	0
+ 621	1
+ 552	8
+ 825	1
+ 455	5
+ 373	0
+ 457	1
+ 813	2
+ 151	6
+ 169	6
+ 243	3
+ 161	4
+ 314	8
+ 508	3
+ 166	8
+ 92	2
+ 856	7
+ 259	4
+ 561	1
+ 467	0
+ 600	8
+ 24	1
+ 961	8
+ 289	1
+ 467	5
+ 679	7
+ 806	8
+ 124	1
+ 621	6
+ 441	8
+ 453	5
+ 954	3
+ 245	2
+ 716	8
+ 297	2
+ 823	9
+ 22	8
+ 955	10
+ 684	2
+ 95	2
+ 702	8
+ 862	5
+ 615	10
+ 628	2
+ 617	1
+ 23	1
+ 602	10
+ 378	8
+ 189	1
+ 654	5
+ 276	5
+ 383	3
+ 323	3
+ 281	0
+ 582	4
+ 159	3
+ 151	0
+ 793	8
+ 6	4
+ 2	6
+ 491	0
+ 693	1
+ 2	1
+ 941	2
+ 164	6
+ 677	4
+ 71	1
+ 737	4
+ 398	0
+ 402	10
+ 395	6
+ 264	5
+ 581	1
+ 312	6
+ 477	3
+ 209	10
+ 340	9
+ 61	3
+ 972	0
+ 532	1
+ 596	2
+ 576	7
+ 269	3
+ 61	7
+ 331	5
+ 647	7
+ 23	9
+ 272	6
+ 967	6
+ 190	4
+ 899	4
+ 413	2
+ 300	5
+ 581	3
+ 475	1
+ 408	1
+ 323	10
+ 738	6
+ 297	8
+ 259	6
+ 262	9
+ 354	3
+ 817	6
+ 890	8
+ 211	1
+ 230	1
+ 479	6
+ 350	8
+ 116	9
+ 52	6
+ 44	5
+ 662	4
+ 443	4
+ 959	7
+ 200	2
+ 368	5
+ 124	7
+ 748	9
+ 349	6
+ 727	6
+ 718	10
+ 671	2
+ 599	0
+ 977	7
+ 952	0
+ 307	10
+ 488	10
+ 363	9
+ 369	3
+ 672	6
+ 540	0
+ 31	7
+ 763	8
+ 606	1
+ 417	3
+ 672	1
+ 289	3
+ 333	9
+ 364	3
+ 604	3
+ 339	9
+ 311	8
+ 879	7
+ 759	2
+ 996	4
+ 817	5
+ 471	8
+ 199	2
+ 627	8
+ 346	0
+ 138	0
+ 180	4
+ 362	5
+ 316	7
+ 823	9
+ 41	2
+ 830	4
+ 989	7
+ 26	7
+ 957	0
+ 179	8
+ 557	7
+ 622	8
+ 885	2
+ 562	2
+ 294	7
+ 250	5
+ 128	6
+ 987	4
+ 337	8
+ 363	4
+ 972	2
+ 730	10
+ 901	8
+ 710	9
+ 777	9
+ 632	3
+ 540	3
+ 91	4
+ 503	7
+ 656	8
+ 353	9
+ 271	5
+ 517	3
+ 924	9
+ 68	3
+ 232	0
+ 480	10
+ 2	4
+ 717	7
+ 240	5
+ 600	9
+ 829	1
+ 126	9
+ 564	6
+ 573	2
+ 426	9
+ 126	7
+ 406	6
+ 955	3
+ 498	0
+ 618	7
+ 62	1
+ 692	1
+ 481	4
+ 775	7
+ 904	4
+ 592	7
+ 515	7
+ 652	1
+ 347	2
+ 300	8
+ 150	4
+ 471	6
+ 69	4
+ 887	6
+ 448	5
+ 297	5
+ 605	10
+ 574	1
+ 398	3
+ 806	3
+ 725	4
+ 34	2
+ 116	7
+ 320	5
+ 911	6
+ 237	1
+ 46	7
+ 619	1
+ 133	5
+ 682	6
+ 12	10
+ 91	6
+ 967	7
+ 702	4
+ 14	5
+ 667	7
+ 905	7
+ 978	0
+ 387	3
+ 484	3
+ 918	7
+ 361	10
+ 429	10
+ 78	6
+ 485	8
+ 143	5
+ 738	2
+ 113	7
+ 899	8
+ 70	9
+ 322	7
+ 651	2
+ 438	6
+ 247	8
+ 927	7
+ 124	8
+ 453	5
+ 808	9
+ 464	9
+ 445	9
+ 646	6
+ 446	4
+ 822	6
+ 89	7
+ 374	2
+ 633	7
+ 897	3
+ 923	3
+ 913	2
+ 160	8
+ 902	3
+ 684	4
+ 768	5
+ 237	2
+ 378	7
+ 181	0
+ 270	6
+ 408	1
+ 187	5
+ 814	6
+ 657	4
+ 257	6
+ 731	2
+ 889	6
+ 350	0
+ 484	3
+ 333	2
+ 607	1
+ 661	8
+ 333	0
+ 527	5
+ 63	8
+ 142	5
+ 890	3
+ 968	7
+ 889	6
+ 151	1
+ 179	9
+ 325	1
+ 526	7
+ 116	0
+ 927	4
+ 178	5
+ 550	8
+ 379	9
+ 877	9
+ 398	9
+ 703	5
+ 410	6
+ 868	4
+ 297	8
+ 3	4
+ 903	2
+ 329	2
+ 250	9
+ 903	4
+ 865	8
+ 815	0
+ 366	4
+ 881	7
+ 248	8
+ 651	6
+ 698	4
+ 185	1
+ 947	1
+ 487	2
+ 810	5
+ 691	7
+ 672	0
+ 940	9
+ 875	8
+ 287	7
diff --git a/contrib/bloom/expected/bloom.out b/contrib/bloom/expected/bloom.out
new file mode 100644
index ...f7334fa
*** a/contrib/bloom/expected/bloom.out
--- b/contrib/bloom/expected/bloom.out
***************
*** 0 ****
--- 1,120 ----
+ CREATE EXTENSION bloom;
+ CREATE TABLE tst (
+ 	i	int4,
+ 	t	text
+ );
+ \copy tst from 'data/data'
+ CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+ SET enable_seqscan=on;
+ SET enable_bitmapscan=off;
+ SET enable_indexscan=off;
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16;
+                 QUERY PLAN                 
+ -------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: (i = 16)
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: (i = 16)
+ (5 rows)
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE t = '5';
+                 QUERY PLAN                 
+ -------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: (t = '5'::text)
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: (t = '5'::text)
+ (5 rows)
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+                         QUERY PLAN                        
+ ----------------------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: ((i = 16) AND (t = '5'::text))
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: ((i = 16) AND (t = '5'::text))
+ (5 rows)
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ VACUUM ANALYZE tst;
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ VACUUM FULL tst;
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ RESET enable_seqscan;
+ RESET enable_bitmapscan;
+ RESET enable_indexscan;
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
new file mode 100644
index ...e5c7a86
*** a/contrib/bloom/sql/bloom.sql
--- b/contrib/bloom/sql/bloom.sql
***************
*** 0 ****
--- 1,46 ----
+ CREATE EXTENSION bloom;
+ 
+ CREATE TABLE tst (
+ 	i	int4,
+ 	t	text
+ );
+ 
+ \copy tst from 'data/data'
+ 
+ CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+ 
+ SET enable_seqscan=on;
+ SET enable_bitmapscan=off;
+ SET enable_indexscan=off;
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16;
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE t = '5';
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ VACUUM ANALYZE tst;
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ VACUUM FULL tst;
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ RESET enable_seqscan;
+ RESET enable_bitmapscan;
+ RESET enable_indexscan;
diff --git a/doc/src/sgml/bloom.sgml b/doc/src/sgml/bloom.sgml
new file mode 100644
index ...c207e6d
*** a/doc/src/sgml/bloom.sgml
--- b/doc/src/sgml/bloom.sgml
***************
*** 0 ****
--- 1,218 ----
+ <!-- doc/src/sgml/bloom.sgml -->
+ 
+ <sect1 id="bloom" xreflabel="bloom">
+  <title>bloom</title>
+ 
+  <indexterm zone="bloom">
+   <primary>bloom</primary>
+  </indexterm>
+ 
+  <para>
+   <literal>bloom</> is a contrib which implements index access method.  It comes
+   as example of custom access methods and generic WAL records usage.  But it
+   is also useful itself.
+  </para>
+ 
+  <sect2>
+   <title>Introduction</title>
+ 
+   <para>
+    Implementation of
+    <ulink url="http://en.wikipedia.org/wiki/Bloom_filter">Bloom filter</ulink>
+    allows fast exclusion of non-candidate tuples.
+    Since signature is a lossy representation of all indexed attributes, 
+    search results should be rechecked using heap information. 
+    User can specify signature length (in uint16, default is 5) and the number of 
+    bits, which can be setted, per attribute (1 < colN < 2048).
+   </para>
+ 
+   <para>
+    This index is useful if table has many attributes and queries can include
+    their arbitary combinations.  Traditional <literal>btree</> index is faster
+    than bloom index, but it'd require too many indexes to support all possible 
+    queries, while one need only one bloom index.  Bloom index supports only 
+    equality comparison.  Since it's a signature file, not a tree, it always
+    should be readed fully, but sequentially, so index search performance is 
+    constant and doesn't depend on a query. 
+   </para>
+  </sect2>
+ 
+  <sect2>
+   <title>Parameters</title>
+ 
+   <para>
+    <literal>bloom</> indexes accept following parameters in <literal>WITH</>
+    clause.
+   </para>
+ 
+    <variablelist>
+    <varlistentry>
+     <term><literal>length</></term>
+     <listitem>
+      <para>
+       Length of signature in uint16 type values
+      </para>
+     </listitem>
+    </varlistentry>
+    </variablelist>
+    <variablelist>
+    <varlistentry>
+     <term><literal>col1 &mdash; col16</></term>
+     <listitem>
+      <para>
+       Number of bits for corresponding column
+      </para>
+     </listitem>
+    </varlistentry>
+    </variablelist>
+  </sect2>
+ 
+  <sect2>
+   <title>Examples</title>
+ 
+   <para>
+    Example of index definition is given below.
+   </para>
+ 
+ <programlisting>
+ CREATE INDEX bloomidx ON tbloom(i1,i2,i3) 
+        WITH (length=5, col1=2, col2=2, col3=4);
+ </programlisting>
+ 
+   <para>
+    Here, we create bloom index with signature length 80 bits and attributes
+    i1, i2  mapped to 2 bits, attribute i3 - to 4 bits.
+   </para>
+ 
+   <para>
+    Example of index definition and usage is given below.
+   </para>
+ 
+ <programlisting>
+ CREATE TABLE tbloom AS
+ SELECT
+     random()::int as i1,
+     random()::int as i2,
+     random()::int as i3,
+     random()::int as i4,
+     random()::int as i5,
+     random()::int as i6,
+     random()::int as i7,
+     random()::int as i8,
+     random()::int as i9,
+     random()::int as i10,
+     random()::int as i11,
+     random()::int as i12,
+     random()::int as i13
+ FROM
+     generate_series(1,1000);
+ CREATE INDEX bloomidx ON tbloom USING
+              bloom (i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12);
+ SELECT pg_relation_size('bloomidx');
+ CREATE index btree_idx ON tbloom(i1,i2,i3,i4,i5,i6,i7,i8,i9,i10,i11,i12);
+ SELECT pg_relation_size('btree_idx');
+ </programlisting>
+ 
+ <programlisting>
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                                    QUERY PLAN
+ -----------------------------------------------------------------------------------------------------------------
+  Bitmap Heap Scan on tbloom  (cost=1.50..5.52 rows=1 width=52) (actual time=0.057..0.057 rows=0 loops=1)
+    Recheck Cond: ((i2 = 20) AND (i10 = 15))
+    ->  Bitmap Index Scan on bloomidx  (cost=0.00..1.50 rows=1 width=0) (actual time=0.041..0.041 rows=9 loops=1)
+          Index Cond: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.081 ms
+ (5 rows)
+ </programlisting>
+ 
+   <para>
+    Seqscan is slow.
+   </para>
+ 
+ <programlisting>
+ =# SET enable_bitmapscan = off;
+ =# SET enable_indexscan = off;
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                             QUERY PLAN
+ --------------------------------------------------------------------------------------------------
+  Seq Scan on tbloom  (cost=0.00..25.00 rows=1 width=52) (actual time=0.162..0.162 rows=0 loops=1)
+    Filter: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.181 ms
+ (3 rows)
+ </programlisting>
+ 
+  <para>
+   Btree index will be not used for this query.
+  </para>
+ 
+ <programlisting>
+ =# DROP INDEX bloomidx;
+ =# CREATE INDEX btree_idx ON tbloom(i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12);
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                             QUERY PLAN
+ --------------------------------------------------------------------------------------------------
+  Seq Scan on tbloom (cost=0.00..25.00 rows=1 width=52) (actual time=0.210..0.210 rows=0 loops=1)
+    Filter: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.250 ms
+ (3 rows)
+ </programlisting>
+  </sect2>
+ 
+  <sect2>
+   <title>Opclass interface</title>
+ 
+   <para>
+    Bloom opclass interface is simple.  It requires 1 supporting function:
+    hash function for indexing datatype.  And it provides 1 search operator:
+    equality operator.  The example below shows <literal>opclass</> definition
+    for <literal>text</> datatype.
+   </para>
+ 
+ <programlisting>
+ CREATE OPERATOR CLASS text_ops
+ DEFAULT FOR TYPE text USING bloom AS
+     OPERATOR    1   =(text, text),
+     FUNCTION    1   hashtext(text);
+ </programlisting>
+  </sect2>
+ 
+  <sect2>
+   <title>Limitation</title>
+   <para>
+ 
+    <itemizedlist>
+     <listitem>
+      <para>
+       For now, only opclasses for <literal>int4</>, <literal>text</> comes
+       with contrib.  However, users may define more of them.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       Only <literal>=</literal> operator is supported for search now.  But it's
+       possible to add support of arrays with contains and intersection
+       operations in future.
+      </para>
+     </listitem>
+    </itemizedlist>
+   </para>
+  </sect2>
+ 
+  <sect2>
+   <title>Authors</title>
+ 
+   <para>
+    Teodor Sigaev <email>teodor@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+ 
+   <para>
+    Alexander Korotkov <email>a.korotkov@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+ 
+   <para>
+    Oleg Bartunov <email>obartunov@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+  </sect2>
+ 
+ </sect1>
diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
new file mode 100644
index 1b3d2d9..c38b2f9
*** a/doc/src/sgml/contrib.sgml
--- b/doc/src/sgml/contrib.sgml
*************** CREATE EXTENSION <replaceable>module_nam
*** 105,110 ****
--- 105,111 ----
   &adminpack;
   &auth-delay;
   &auto-explain;
+  &bloom;
   &btree-gin;
   &btree-gist;
   &chkpass;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
new file mode 100644
index a12fee7..4a93ec2
*** a/doc/src/sgml/filelist.sgml
--- b/doc/src/sgml/filelist.sgml
***************
*** 106,111 ****
--- 106,112 ----
  <!ENTITY adminpack       SYSTEM "adminpack.sgml">
  <!ENTITY auth-delay      SYSTEM "auth-delay.sgml">
  <!ENTITY auto-explain    SYSTEM "auto-explain.sgml">
+ <!ENTITY bloom           SYSTEM "bloom.sgml">
  <!ENTITY btree-gin       SYSTEM "btree-gin.sgml">
  <!ENTITY btree-gist      SYSTEM "btree-gist.sgml">
  <!ENTITY chkpass         SYSTEM "chkpass.sgml">
#45Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Alexander Korotkov (#44)
3 attachment(s)
Re: WIP: Access method extendability

On Mon, Feb 15, 2016 at 5:30 PM, Alexander Korotkov <
a.korotkov@postgrespro.ru> wrote:

General notes about current version of patch:
* A lot of comments added.
* bloom documentation is moved from README to SGML with a lot of addons
and cleanup.
* Memory move support in generic xlog is removed. Now it's much more
simple and clean.
* Tests for CREATE ACCESS METHOD added. For now, it creates a mirror of
GiST access method.
* Syntax for CREATE ACCESS METHOD is changed. For now, it's "CREATE ACCESS
METHOD amname TYPE INDEX HANDLER handler;" in respect of parallel work on
sequential access methods. New access method attribute added: amtype.

Next version of patch is attached:
* Documentation for CREATE ACCESS METHOD command is added.
* Refactoring and comments for bloom contrib.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

Attachments:

create-am.7.patchapplication/octet-stream; name=create-am.7.patchDownload
diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml
new file mode 100644
index 5f7befb..e5f8f1c
*** a/doc/src/sgml/indexam.sgml
--- b/doc/src/sgml/indexam.sgml
***************
*** 58,63 ****
--- 58,69 ----
    </para>
  
    <para>
+    Index access access methods can be defined and dropped using
+    <xref linkend="sql-createaccessmethod"> and
+     <xref linkend="sql-dropaccessmethod"> SQL commands respectively.
+   </para>
+ 
+   <para>
     An index access method handler function must be declared to accept a
     single argument of type <type>internal</> and to return the
     pseudo-type <type>index_am_handler</>.  The argument is a dummy value that
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
new file mode 100644
index bf95453..77667bd
*** a/doc/src/sgml/ref/allfiles.sgml
--- b/doc/src/sgml/ref/allfiles.sgml
*************** Complete list of usable sgml source file
*** 52,57 ****
--- 52,58 ----
  <!ENTITY commit             SYSTEM "commit.sgml">
  <!ENTITY commitPrepared     SYSTEM "commit_prepared.sgml">
  <!ENTITY copyTable          SYSTEM "copy.sgml">
+ <!ENTITY createAccessMethod SYSTEM "create_access_method.sgml">
  <!ENTITY createAggregate    SYSTEM "create_aggregate.sgml">
  <!ENTITY createCast         SYSTEM "create_cast.sgml">
  <!ENTITY createCollation    SYSTEM "create_collation.sgml">
*************** Complete list of usable sgml source file
*** 94,99 ****
--- 95,101 ----
  <!ENTITY delete             SYSTEM "delete.sgml">
  <!ENTITY discard            SYSTEM "discard.sgml">
  <!ENTITY do                 SYSTEM "do.sgml">
+ <!ENTITY dropAccessMethod   SYSTEM "drop_access_method.sgml">
  <!ENTITY dropAggregate      SYSTEM "drop_aggregate.sgml">
  <!ENTITY dropCast           SYSTEM "drop_cast.sgml">
  <!ENTITY dropCollation      SYSTEM "drop_collation.sgml">
diff --git a/doc/src/sgml/ref/create_access_method.sgml b/doc/src/sgml/ref/create_access_method.sgml
new file mode 100644
index ...5a71dc3
*** a/doc/src/sgml/ref/create_access_method.sgml
--- b/doc/src/sgml/ref/create_access_method.sgml
***************
*** 0 ****
--- 1,120 ----
+ <!--
+ doc/src/sgml/ref/create_access_method.sgml
+ PostgreSQL documentation
+ -->
+ 
+ <refentry id="SQL-CREATEACCESSMETHOD">
+  <indexterm zone="sql-createaccessmethod">
+   <primary>CREATE ACCESS METHOD</primary>
+  </indexterm>
+ 
+  <refmeta>
+   <refentrytitle>CREATE ACCESS METHOD</refentrytitle>
+   <manvolnum>7</manvolnum>
+   <refmiscinfo>SQL - Language Statements</refmiscinfo>
+  </refmeta>
+ 
+  <refnamediv>
+   <refname>CREATE ACCESS METHOD</refname>
+   <refpurpose>define a new access method</refpurpose>
+  </refnamediv>
+ 
+  <refsynopsisdiv>
+ <synopsis>
+ CREATE ACCESS METHOD <replaceable class="parameter">name</replaceable>
+     TYPE INDEX
+     [ HANDLER <replaceable class="parameter">handler_function</replaceable> | NO HANDLER ]
+ </synopsis>
+  </refsynopsisdiv>
+ 
+  <refsect1>
+   <title>Description</title>
+ 
+   <para>
+    <command>CREATE ACCESS METHOD</command> creates a new access method.
+   </para>
+ 
+   <para>
+    The access method name must be unique within the database.
+   </para>
+ 
+   <para>
+    Only superusers can define new access methods.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Parameters</title>
+ 
+   <variablelist>
+    <varlistentry>
+     <term><replaceable class="parameter">name</replaceable></term>
+     <listitem>
+      <para>
+       The name of the access method to be created.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>TYPE INDEX</literal></term>
+     <listitem>
+      <para>
+       This clause specifies type of access method to define.
+       For now, there are only index access methods.  But intentionally
+       there would be other types of access methods.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>HANDLER <replaceable class="parameter">handler_function</replaceable></literal></term>
+     <listitem>
+      <para><replaceable class="parameter">handler_function</replaceable> is the
+       name of a previously registered function that will be called to
+       retrieve the struct which contains required parameters and functions
+       of access method to the core.  The handler function must take single
+       argument of type <type>internal</>, and its return type must be
+       <type>index_am_handler</type>.
+      </para>
+ 
+      <para>
+       See <xref linkend="index-api"> for index access methods API.
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Examples</title>
+ 
+   <para>
+    Create an access method <literal>bloom</> with
+    handler function <literal>blhandler</>:
+ <programlisting>
+ CREATE ACCESS METHOD bloom TYPE INDEX HANDLER blhandler;
+ </programlisting>
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Compatibility</title>
+ 
+   <para>
+    <command>CREATE ACCESS METHOD</command> is a
+    <productname>PostgreSQL</> extension.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>See Also</title>
+ 
+   <simplelist type="inline">
+    <member><xref linkend="sql-dropaccessmethod"></member>
+    <member><xref linkend="sql-createopclass"></member>
+    <member><xref linkend="sql-createopfamily"></member>
+   </simplelist>
+  </refsect1>
+ 
+ </refentry>
diff --git a/doc/src/sgml/ref/drop_access_method.sgml b/doc/src/sgml/ref/drop_access_method.sgml
new file mode 100644
index ...354b923
*** a/doc/src/sgml/ref/drop_access_method.sgml
--- b/doc/src/sgml/ref/drop_access_method.sgml
***************
*** 0 ****
--- 1,112 ----
+ <!--
+ doc/src/sgml/ref/drop_access_method.sgml
+ PostgreSQL documentation
+ -->
+ 
+ <refentry id="SQL-DROPACCESSMETHOD">
+  <indexterm zone="sql-dropaccessmethod">
+   <primary>DROP ACCESS METHOD</primary>
+  </indexterm>
+ 
+  <refmeta>
+   <refentrytitle>DROP ACCESS METHOD</refentrytitle>
+   <manvolnum>7</manvolnum>
+   <refmiscinfo>SQL - Language Statements</refmiscinfo>
+  </refmeta>
+ 
+  <refnamediv>
+   <refname>DROP ACCESS METHOD</refname>
+   <refpurpose>remove an access method</refpurpose>
+  </refnamediv>
+ 
+  <refsynopsisdiv>
+ <synopsis>
+ DROP ACCESS METHOD [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ CASCADE | RESTRICT ]
+ </synopsis>
+  </refsynopsisdiv>
+ 
+  <refsect1>
+   <title>Description</title>
+ 
+   <para>
+    <command>DROP ACCESS METHOD</command> removes an existing access method.
+    Only superusers can drop access methods.
+   </para>
+ 
+   <para>
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Parameters</title>
+ 
+   <variablelist>
+    <varlistentry>
+     <term><literal>IF EXISTS</literal></term>
+     <listitem>
+      <para>
+       Do not throw an error if the access method does not exist.
+       A notice is issued in this case.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><replaceable class="parameter">name</replaceable></term>
+     <listitem>
+      <para>
+       The name of an existing access method.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>CASCADE</literal></term>
+     <listitem>
+      <para>
+       Automatically drop objects that depend on the access method
+       (such as operator classes, operator families, indexes).
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>RESTRICT</literal></term>
+     <listitem>
+      <para>
+       Refuse to drop the access method if any objects depend on it.
+       This is the default.
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Examples</title>
+ 
+   <para>
+    Drop the access method <literal>bloom</>:
+ <programlisting>
+ DROP ACCESS METHOD bloom;
+ </programlisting></para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Compatibility</title>
+ 
+   <para>
+    <command>DROP ACCESS METHOD</command> is a
+    <productname>PostgreSQL</> extension.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>See Also</title>
+ 
+   <simplelist type="inline">
+    <member><xref linkend="sql-createaccessmethod"></member>
+   </simplelist>
+  </refsect1>
+ 
+ </refentry>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
new file mode 100644
index 03020df..8acdff1
*** a/doc/src/sgml/reference.sgml
--- b/doc/src/sgml/reference.sgml
***************
*** 80,85 ****
--- 80,86 ----
     &commit;
     &commitPrepared;
     &copyTable;
+    &createAccessMethod;
     &createAggregate;
     &createCast;
     &createCollation;
***************
*** 122,127 ****
--- 123,129 ----
     &delete;
     &discard;
     &do;
+    &dropAccessMethod;
     &dropAggregate;
     &dropCast;
     &dropCollation;
diff --git a/src/backend/access/index/amapi.c b/src/backend/access/index/amapi.c
new file mode 100644
index bda166a..053d3bb
*** a/src/backend/access/index/amapi.c
--- b/src/backend/access/index/amapi.c
*************** GetIndexAmRoutineByAmId(Oid amoid)
*** 62,67 ****
--- 62,74 ----
  			 amoid);
  	amform = (Form_pg_am) GETSTRUCT(tuple);
  
+ 	/* Check if it's index access method */
+ 	if (amform->amtype != 'i')
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("access method \"%s\" type is not index",
+ 						NameStr(amform->amname))));
+ 
  	amhandler = amform->amhandler;
  
  	/* Complain if handler OID is invalid */
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
new file mode 100644
index c48e37b..a86a488
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 20,25 ****
--- 20,26 ----
  #include "catalog/heap.h"
  #include "catalog/index.h"
  #include "catalog/objectaccess.h"
+ #include "catalog/pg_am.h"
  #include "catalog/pg_amop.h"
  #include "catalog/pg_amproc.h"
  #include "catalog/pg_attrdef.h"
*************** static const Oid object_classes[] = {
*** 160,166 ****
  	ExtensionRelationId,		/* OCLASS_EXTENSION */
  	EventTriggerRelationId,		/* OCLASS_EVENT_TRIGGER */
  	PolicyRelationId,			/* OCLASS_POLICY */
! 	TransformRelationId			/* OCLASS_TRANSFORM */
  };
  
  
--- 161,168 ----
  	ExtensionRelationId,		/* OCLASS_EXTENSION */
  	EventTriggerRelationId,		/* OCLASS_EVENT_TRIGGER */
  	PolicyRelationId,			/* OCLASS_POLICY */
! 	TransformRelationId,		/* OCLASS_TRANSFORM */
! 	AccessMethodRelationId		/* OCLASS_AM */
  };
  
  
*************** doDeletion(const ObjectAddress *object, 
*** 1270,1275 ****
--- 1272,1280 ----
  
  		case OCLASS_TRANSFORM:
  			DropTransformById(object->objectId);
+ 
+ 		case OCLASS_AM:
+ 			RemoveAccessMethodById(object->objectId);
  			break;
  
  		default:
*************** getObjectClass(const ObjectAddress *obje
*** 2415,2420 ****
--- 2420,2428 ----
  
  		case TransformRelationId:
  			return OCLASS_TRANSFORM;
+ 
+ 		case AccessMethodRelationId:
+ 			return OCLASS_AM;
  	}
  
  	/* shouldn't get here */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
new file mode 100644
index d2aaa6d..83e8b24
*** a/src/backend/catalog/objectaddress.c
--- b/src/backend/catalog/objectaddress.c
*************** static const ObjectPropertyType ObjectPr
*** 438,443 ****
--- 438,455 ----
  		Anum_pg_type_typacl,
  		ACL_KIND_TYPE,
  		true
+ 	},
+ 	{
+ 		AccessMethodRelationId,
+ 		AmOidIndexId,
+ 		AMOID,
+ 		AMNAME,
+ 		Anum_pg_am_amname,
+ 		InvalidAttrNumber,
+ 		InvalidAttrNumber,
+ 		InvalidAttrNumber,
+ 		-1,
+ 		true
  	}
  };
  
*************** static ObjectAddress get_object_address_
*** 674,679 ****
--- 686,693 ----
  							   List *objargs, bool missing_ok);
  static ObjectAddress get_object_address_defacl(List *objname, List *objargs,
  						  bool missing_ok);
+ static ObjectAddress get_object_address_am(ObjectType objtype, List *objname,
+ 						bool missing_ok);
  static const ObjectPropertyType *get_object_property_data(Oid class_id);
  
  static void getRelationDescription(StringInfo buffer, Oid relid);
*************** get_object_address(ObjectType objtype, L
*** 913,918 ****
--- 927,935 ----
  				address = get_object_address_defacl(objname, objargs,
  													missing_ok);
  				break;
+ 			case OBJECT_ACCESS_METHOD:
+ 				address = get_object_address_am(objtype, objname, missing_ok);
+ 				break;
  			default:
  				elog(ERROR, "unrecognized objtype: %d", (int) objtype);
  				/* placate compiler, in case it thinks elog might return */
*************** get_object_address_opcf(ObjectType objty
*** 1489,1495 ****
  	ObjectAddress address;
  
  	/* XXX no missing_ok support here */
! 	amoid = get_am_oid(strVal(linitial(objname)), false);
  	objname = list_copy_tail(objname, 1);
  
  	switch (objtype)
--- 1506,1512 ----
  	ObjectAddress address;
  
  	/* XXX no missing_ok support here */
! 	amoid = get_am_oid(strVal(linitial(objname)), 'i', false);
  	objname = list_copy_tail(objname, 1);
  
  	switch (objtype)
*************** get_object_address_opcf(ObjectType objty
*** 1516,1521 ****
--- 1533,1597 ----
  }
  
  /*
+  * Find the ObjectAddress for an access method.
+  */
+ static ObjectAddress
+ get_object_address_am(ObjectType objtype, List *objname, bool missing_ok)
+ {
+ 	ObjectAddress	address;
+ 	char		   *amname, *catalogname;
+ 	Type		tup;
+ 
+ 	switch (list_length(objname))
+ 	{
+ 		case 1:
+ 			amname = strVal(linitial(objname));
+ 			break;
+ 		case 2:
+ 			catalogname = strVal(linitial(objname));
+ 			amname = strVal(lsecond(objname));
+ 
+ 			/*
+ 			 * We check the catalog name and then ignore it.
+ 			 */
+ 			if (strcmp(catalogname, get_database_name(MyDatabaseId)) != 0)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				  errmsg("cross-database references are not implemented: %s",
+ 						 NameListToString(objname))));
+ 			break;
+ 		default:
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_SYNTAX_ERROR),
+ 				errmsg("improper access method name (too many dotted names): %s",
+ 					   NameListToString(objname))));
+ 			break;
+ 	}
+ 
+ 	address.classId = AccessMethodRelationId;
+ 	address.objectId = InvalidOid;
+ 	address.objectSubId = 0;
+ 
+ 	tup = SearchSysCache1(AMNAME, PointerGetDatum(amname));
+ 	if (!HeapTupleIsValid(tup))
+ 	{
+ 		/* Access method is missing, report error if needed */
+ 		if (!missing_ok)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 					 errmsg("access method \"%s\" does not exist",
+ 							amname)));
+ 		return address;
+ 	}
+ 
+ 	/* Access method is found, return its oid */
+ 	address.objectId = HeapTupleGetOid(tup);
+ 	ReleaseSysCache(tup);
+ 
+ 	return address;
+ }
+ 
+ /*
   * Find the ObjectAddress for an opclass/opfamily member.
   *
   * (The returned address corresponds to a pg_amop/pg_amproc object).
*************** check_object_ownership(Oid roleid, Objec
*** 2179,2184 ****
--- 2255,2261 ----
  			break;
  		case OBJECT_TSPARSER:
  		case OBJECT_TSTEMPLATE:
+ 		case OBJECT_ACCESS_METHOD:
  			/* We treat these object types as being owned by superusers */
  			if (!superuser_arg(roleid))
  				ereport(ERROR,
*************** getObjectDescription(const ObjectAddress
*** 3129,3134 ****
--- 3206,3226 ----
  				break;
  			}
  
+ 		case OCLASS_AM:
+ 			{
+ 				HeapTuple	tup;
+ 
+ 				tup = SearchSysCache1(AMOID,
+ 									  ObjectIdGetDatum(object->objectId));
+ 				if (!HeapTupleIsValid(tup))
+ 					elog(ERROR, "cache lookup failed for access method %u",
+ 						 object->objectId);
+ 				appendStringInfo(&buffer, _("access method %s"),
+ 							NameStr(((Form_pg_am) GETSTRUCT(tup))->amname));
+ 				ReleaseSysCache(tup);
+ 				break;
+ 			}
+ 
  		default:
  			appendStringInfo(&buffer, "unrecognized object %u %u %d",
  							 object->classId,
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
new file mode 100644
index b1ac704..6b3742c
*** a/src/backend/commands/Makefile
--- b/src/backend/commands/Makefile
*************** subdir = src/backend/commands
*** 12,18 ****
  top_builddir = ../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o  \
  	collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
  	dbcommands.o define.o discard.o dropcmds.o \
  	event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
--- 12,18 ----
  top_builddir = ../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
  	collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
  	dbcommands.o define.o discard.o dropcmds.o \
  	event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
new file mode 100644
index ...256d5e2
*** a/src/backend/commands/amcmds.c
--- b/src/backend/commands/amcmds.c
***************
*** 0 ****
--- 1,289 ----
+ /*-------------------------------------------------------------------------
+  *
+  * amcmds.c
+  *	  Routines for SQL commands that manipulate access methods.
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *
+  * IDENTIFICATION
+  *	  src/backend/commands/amcmds.c
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "access/heapam.h"
+ #include "access/htup_details.h"
+ #include "access/xact.h"
+ #include "catalog/binary_upgrade.h"
+ #include "catalog/catalog.h"
+ #include "catalog/dependency.h"
+ #include "catalog/heap.h"
+ #include "catalog/indexing.h"
+ #include "catalog/objectaccess.h"
+ #include "catalog/pg_authid.h"
+ #include "catalog/pg_am.h"
+ #include "catalog/pg_collation.h"
+ #include "catalog/pg_constraint.h"
+ #include "catalog/pg_depend.h"
+ #include "catalog/pg_enum.h"
+ #include "catalog/pg_language.h"
+ #include "catalog/pg_namespace.h"
+ #include "catalog/pg_proc.h"
+ #include "catalog/pg_proc_fn.h"
+ #include "catalog/pg_range.h"
+ #include "catalog/pg_type.h"
+ #include "catalog/pg_type_fn.h"
+ #include "commands/dbcommands.h"
+ #include "commands/defrem.h"
+ #include "commands/tablecmds.h"
+ #include "commands/typecmds.h"
+ #include "executor/executor.h"
+ #include "miscadmin.h"
+ #include "nodes/makefuncs.h"
+ #include "optimizer/planner.h"
+ #include "optimizer/var.h"
+ #include "parser/parse_coerce.h"
+ #include "parser/parse_collate.h"
+ #include "parser/parse_expr.h"
+ #include "parser/parse_func.h"
+ #include "parser/parse_type.h"
+ #include "utils/acl.h"
+ #include "utils/builtins.h"
+ #include "utils/fmgroids.h"
+ #include "utils/lsyscache.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ #include "utils/snapmgr.h"
+ #include "utils/syscache.h"
+ #include "utils/tqual.h"
+ 
+ /*
+  * Convert a handler function name passed from the parser to an Oid.
+  */
+ static Oid
+ lookup_index_am_handler_func(List *handler_name)
+ {
+ 	Oid			handlerOid;
+ 	Oid			funcargtypes[1] = {INTERNALOID};
+ 
+ 	if (handler_name == NIL)
+ 		return InvalidOid;
+ 
+ 	/* handlers have no arguments */
+ 	handlerOid = LookupFuncName(handler_name, 1, funcargtypes, false);
+ 
+ 	/* check that handler has correct return type */
+ 	if (get_func_rettype(handlerOid) != INDEX_AM_HANDLEROID)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 				 errmsg("function %s must return type \"index_am_handler\"",
+ 						NameListToString(handler_name))));
+ 
+ 	return handlerOid;
+ }
+ 
+ 
+ /*
+  * CreateAcessMethod
+  *		Registers a new access method.
+  */
+ ObjectAddress
+ CreateAccessMethod(CreateAmStmt *stmt)
+ {
+ 	Relation		rel;
+ 	ObjectAddress	myself;
+ 	ObjectAddress	referenced;
+ 	Oid				amoid;
+ 	Oid				amhandler;
+ 	bool			nulls[Natts_pg_am];
+ 	Datum			values[Natts_pg_am];
+ 	HeapTuple		tup;
+ 
+ 	rel = heap_open(AccessMethodRelationId, RowExclusiveLock);
+ 
+ 	/* Must be super user */
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 			errmsg("permission denied to create access method \"%s\"",
+ 				   stmt->amname),
+ 			errhint("Must be superuser to create access method.")));
+ 
+ 	/* Check if name is busy */
+ 	amoid = GetSysCacheOid1(AMNAME, CStringGetDatum(stmt->amname));
+ 	if (OidIsValid(amoid))
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_DUPLICATE_OBJECT),
+ 				 errmsg("access method \"%s\" already exists", stmt->amname)));
+ 	}
+ 
+ 	/*
+ 	 * Get handler function oid. Handler signature depends on access method
+ 	 * type.
+ 	 */
+ 	switch(stmt->amtype)
+ 	{
+ 		case 'i':
+ 			amhandler = lookup_index_am_handler_func(stmt->handler_name);
+ 			if (!OidIsValid(amhandler))
+ 			{
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_UNDEFINED_FUNCTION),
+ 						 errmsg("handler function \"%s\" is undefined",
+ 								NameListToString(stmt->handler_name))));
+ 			}
+ 			break;
+ 		default:
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 					 errmsg("wrong access method type \"%c\"", stmt->amtype)));
+ 			break;
+ 	}
+ 
+ 	/*
+ 	 * Insert tuple into pg_am.
+ 	 */
+ 	memset(values, 0, sizeof(values));
+ 	memset(nulls, false, sizeof(nulls));
+ 
+ 	values[Anum_pg_am_amname - 1] =
+ 		DirectFunctionCall1(namein, CStringGetDatum(stmt->amname));
+ 	values[Anum_pg_am_amhandler - 1] = ObjectIdGetDatum(amhandler);
+ 	values[Anum_pg_am_amtype - 1] = CharGetDatum(stmt->amtype);
+ 
+ 	tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+ 
+ 	amoid = simple_heap_insert(rel, tup);
+ 	CatalogUpdateIndexes(rel, tup);
+ 	heap_freetuple(tup);
+ 
+ 	myself.classId = AccessMethodRelationId;
+ 	myself.objectId = amoid;
+ 	myself.objectSubId = 0;
+ 
+ 	/* Record dependecy on handler function */
+ 	referenced.classId = ProcedureRelationId;
+ 	referenced.objectId = amhandler;
+ 	referenced.objectSubId = 0;
+ 
+ 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ 
+ 	recordDependencyOnCurrentExtension(&myself, false);
+ 
+ 	heap_close(rel, RowExclusiveLock);
+ 
+ 	return myself;
+ }
+ 
+ /*
+  * Guts of access method deletion.
+  */
+ void
+ RemoveAccessMethodById(Oid amOid)
+ {
+ 	Relation	relation;
+ 	HeapTuple	tup;
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 errmsg("must be superuser to drop access methods")));
+ 
+ 	relation = heap_open(AccessMethodRelationId, RowExclusiveLock);
+ 
+ 	tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
+ 	if (!HeapTupleIsValid(tup))
+ 		elog(ERROR, "cache lookup failed for access method %u", amOid);
+ 
+ 	simple_heap_delete(relation, &tup->t_self);
+ 
+ 	ReleaseSysCache(tup);
+ 
+ 	heap_close(relation, RowExclusiveLock);
+ }
+ 
+ /*
+  * Convert single charater access method type into string for error reporting.
+  */
+ static char *
+ get_am_type_string(char amtype)
+ {
+ 	switch (amtype)
+ 	{
+ 		case 'i':
+ 			return "index";
+ 		default:
+ 			elog(ERROR, "invalid access method type '%c'", amtype);
+ 	}
+ }
+ 
+ /*
+  * get_am_oid - given an access method name and type, look up the OID
+  *
+  * If missing_ok is false, throw an error if access method not found.  If
+  * true, just return InvalidOid.
+  */
+ Oid
+ get_am_oid(const char *amname, char amtype, bool missing_ok)
+ {
+ 	HeapTuple	tup;
+ 	Oid			oid = InvalidOid;
+ 
+ 	tup = SearchSysCache1(AMNAME, CStringGetDatum(amname));
+ 	if (HeapTupleIsValid(tup))
+ 	{
+ 		Form_pg_am amform = (Form_pg_am) GETSTRUCT(tup);
+ 
+ 		if (amform->amtype != amtype)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 					 errmsg("access method \"%s\" type is not %s",
+ 							NameStr(amform->amname),
+ 							get_am_type_string(amtype))));
+ 
+ 		oid = HeapTupleGetOid(tup);
+ 		ReleaseSysCache(tup);
+ 	}
+ 
+ 	if (!OidIsValid(oid) && !missing_ok)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 				 errmsg("access method \"%s\" does not exist", amname)));
+ 	return oid;
+ }
+ 
+ /*
+  * get_am_oid - given an access method OID name and type, look up the name
+  *
+  * Access method type is not required for lookup.  However it's useful to check
+  * the type to ensure it is what we're looking for.
+  */
+ char *
+ get_am_name(Oid amOid, char amtype)
+ {
+ 	HeapTuple	tup;
+ 	char	   *result = NULL;
+ 
+ 	tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
+ 	if (HeapTupleIsValid(tup))
+ 	{
+ 		Form_pg_am amform = (Form_pg_am) GETSTRUCT(tup);
+ 
+ 		if (amform->amtype != amtype)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 					 errmsg("access method \"%s\" type is not %s",
+ 							NameStr(amform->amname),
+ 							get_am_type_string(amtype))));
+ 
+ 
+ 		result = pstrdup(NameStr(amform->amname));
+ 		ReleaseSysCache(tup);
+ 	}
+ 	return result;
+ }
+ 
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
new file mode 100644
index 9e32f8d..3f52ad8
*** a/src/backend/commands/event_trigger.c
--- b/src/backend/commands/event_trigger.c
*************** typedef enum
*** 86,91 ****
--- 86,92 ----
  
  /* XXX merge this with ObjectTypeMap? */
  static event_trigger_support_data event_trigger_support[] = {
+ 	{"ACCESS METHOD", true},
  	{"AGGREGATE", true},
  	{"CAST", true},
  	{"CONSTRAINT", true},
*************** EventTriggerSupportsObjectType(ObjectTyp
*** 1078,1083 ****
--- 1079,1085 ----
  		case OBJECT_EVENT_TRIGGER:
  			/* no support for event triggers on event triggers */
  			return false;
+ 		case OBJECT_ACCESS_METHOD:
  		case OBJECT_AGGREGATE:
  		case OBJECT_AMOP:
  		case OBJECT_AMPROC:
*************** EventTriggerSupportsObjectClass(ObjectCl
*** 1167,1172 ****
--- 1169,1175 ----
  		case OCLASS_DEFACL:
  		case OCLASS_EXTENSION:
  		case OCLASS_POLICY:
+ 		case OCLASS_AM:
  			return true;
  	}
  
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
new file mode 100644
index 8a66196..a15f22f
*** a/src/backend/commands/opclasscmds.c
--- b/src/backend/commands/opclasscmds.c
*************** DefineOpClass(CreateOpClassStmt *stmt)
*** 678,683 ****
--- 678,689 ----
  	myself.objectId = opclassoid;
  	myself.objectSubId = 0;
  
+ 	/* dependency on access method */
+ 	referenced.classId = AccessMethodRelationId;
+ 	referenced.objectId = amoid;
+ 	referenced.objectSubId = 0;
+ 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ 
  	/* dependency on namespace */
  	referenced.classId = NamespaceRelationId;
  	referenced.objectId = namespaceoid;
*************** DefineOpFamily(CreateOpFamilyStmt *stmt)
*** 743,749 ****
  					   get_namespace_name(namespaceoid));
  
  	/* Get access method OID, throwing an error if it doesn't exist. */
! 	amoid = get_am_oid(stmt->amname, false);
  
  	/* XXX Should we make any privilege check against the AM? */
  
--- 749,755 ----
  					   get_namespace_name(namespaceoid));
  
  	/* Get access method OID, throwing an error if it doesn't exist. */
! 	amoid = get_am_oid(stmt->amname, 'i', false);
  
  	/* XXX Should we make any privilege check against the AM? */
  
*************** assignOperTypes(OpFamilyMember *member, 
*** 1106,1112 ****
  			ereport(ERROR,
  					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
  			errmsg("access method \"%s\" does not support ordering operators",
! 				   get_am_name(amoid))));
  	}
  	else
  	{
--- 1112,1118 ----
  			ereport(ERROR,
  					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
  			errmsg("access method \"%s\" does not support ordering operators",
! 				   get_am_name(amoid, 'i'))));
  	}
  	else
  	{
*************** RemoveAmProcEntryById(Oid entryOid)
*** 1663,1683 ****
  	heap_close(rel, RowExclusiveLock);
  }
  
- char *
- get_am_name(Oid amOid)
- {
- 	HeapTuple	tup;
- 	char	   *result = NULL;
- 
- 	tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
- 	if (HeapTupleIsValid(tup))
- 	{
- 		result = pstrdup(NameStr(((Form_pg_am) GETSTRUCT(tup))->amname));
- 		ReleaseSysCache(tup);
- 	}
- 	return result;
- }
- 
  /*
   * Subroutine for ALTER OPERATOR CLASS SET SCHEMA/RENAME
   *
--- 1669,1674 ----
*************** IsThereOpClassInNamespace(const char *op
*** 1697,1703 ****
  				(errcode(ERRCODE_DUPLICATE_OBJECT),
  				 errmsg("operator class \"%s\" for access method \"%s\" already exists in schema \"%s\"",
  						opcname,
! 						get_am_name(opcmethod),
  						get_namespace_name(opcnamespace))));
  }
  
--- 1688,1694 ----
  				(errcode(ERRCODE_DUPLICATE_OBJECT),
  				 errmsg("operator class \"%s\" for access method \"%s\" already exists in schema \"%s\"",
  						opcname,
! 						get_am_name(opcmethod, 'i'),
  						get_namespace_name(opcnamespace))));
  }
  
*************** IsThereOpFamilyInNamespace(const char *o
*** 1720,1744 ****
  				(errcode(ERRCODE_DUPLICATE_OBJECT),
  				 errmsg("operator family \"%s\" for access method \"%s\" already exists in schema \"%s\"",
  						opfname,
! 						get_am_name(opfmethod),
  						get_namespace_name(opfnamespace))));
  }
- 
- /*
-  * get_am_oid - given an access method name, look up the OID
-  *
-  * If missing_ok is false, throw an error if access method not found.  If
-  * true, just return InvalidOid.
-  */
- Oid
- get_am_oid(const char *amname, bool missing_ok)
- {
- 	Oid			oid;
- 
- 	oid = GetSysCacheOid1(AMNAME, CStringGetDatum(amname));
- 	if (!OidIsValid(oid) && !missing_ok)
- 		ereport(ERROR,
- 				(errcode(ERRCODE_UNDEFINED_OBJECT),
- 				 errmsg("access method \"%s\" does not exist", amname)));
- 	return oid;
- }
--- 1711,1716 ----
  				(errcode(ERRCODE_DUPLICATE_OBJECT),
  				 errmsg("operator family \"%s\" for access method \"%s\" already exists in schema \"%s\"",
  						opfname,
! 						get_am_name(opfmethod, 'i'),
  						get_namespace_name(opfnamespace))));
  }
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
new file mode 100644
index a9e9cc3..85bd5ac
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copyCreateTransformStmt(const CreateTra
*** 3828,3833 ****
--- 3828,3845 ----
  	return newnode;
  }
  
+ static CreateAmStmt *
+ _copyCreateAmStmt(const CreateAmStmt *from)
+ {
+ 	CreateAmStmt *newnode = makeNode(CreateAmStmt);
+ 
+ 	COPY_STRING_FIELD(amname);
+ 	COPY_NODE_FIELD(handler_name);
+ 	COPY_SCALAR_FIELD(amtype);
+ 
+ 	return newnode;
+ }
+ 
  static CreateTrigStmt *
  _copyCreateTrigStmt(const CreateTrigStmt *from)
  {
*************** copyObject(const void *from)
*** 4819,4824 ****
--- 4831,4839 ----
  		case T_CreateTransformStmt:
  			retval = _copyCreateTransformStmt(from);
  			break;
+ 		case T_CreateAmStmt:
+ 			retval = _copyCreateAmStmt(from);
+ 			break;
  		case T_CreateTrigStmt:
  			retval = _copyCreateTrigStmt(from);
  			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
new file mode 100644
index b9c3959..f19fde1
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
*************** _equalCreateTransformStmt(const CreateTr
*** 1855,1860 ****
--- 1855,1870 ----
  }
  
  static bool
+ _equalCreateAmStmt(const CreateAmStmt *a, const CreateAmStmt *b)
+ {
+ 	COMPARE_STRING_FIELD(amname);
+ 	COMPARE_NODE_FIELD(handler_name);
+ 	COMPARE_SCALAR_FIELD(amtype);
+ 
+ 	return true;
+ }
+ 
+ static bool
  _equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b)
  {
  	COMPARE_STRING_FIELD(trigname);
*************** equal(const void *a, const void *b)
*** 3146,3151 ****
--- 3156,3164 ----
  		case T_CreateTransformStmt:
  			retval = _equalCreateTransformStmt(a, b);
  			break;
+ 		case T_CreateAmStmt:
+ 			retval = _equalCreateAmStmt(a, b);
+ 			break;
  		case T_CreateTrigStmt:
  			retval = _equalCreateTrigStmt(a, b);
  			break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
new file mode 100644
index b307b48..fe95580
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
*************** static Node *makeRecursiveViewSelect(cha
*** 263,269 ****
  		DeallocateStmt PrepareStmt ExecuteStmt
  		DropOwnedStmt ReassignOwnedStmt
  		AlterTSConfigurationStmt AlterTSDictionaryStmt
! 		CreateMatViewStmt RefreshMatViewStmt
  
  %type <node>	select_no_parens select_with_parens select_clause
  				simple_select values_clause
--- 263,269 ----
  		DeallocateStmt PrepareStmt ExecuteStmt
  		DropOwnedStmt ReassignOwnedStmt
  		AlterTSConfigurationStmt AlterTSDictionaryStmt
! 		CreateMatViewStmt RefreshMatViewStmt CreateAmStmt DropAmStmt
  
  %type <node>	select_no_parens select_with_parens select_clause
  				simple_select values_clause
*************** static Node *makeRecursiveViewSelect(cha
*** 604,610 ****
  	LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
  	LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
  
! 	MAPPING MATCH MATERIALIZED MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
  
  	NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NONE
  	NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
--- 604,610 ----
  	LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
  	LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
  
! 	MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE
  
  	NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NONE
  	NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
*************** stmt :
*** 789,794 ****
--- 789,795 ----
  			| CommentStmt
  			| ConstraintsSetStmt
  			| CopyStmt
+ 			| CreateAmStmt
  			| CreateAsStmt
  			| CreateAssertStmt
  			| CreateCastStmt
*************** stmt :
*** 823,828 ****
--- 824,830 ----
  			| DeleteStmt
  			| DiscardStmt
  			| DoStmt
+ 			| DropAmStmt
  			| DropAssertStmt
  			| DropCastStmt
  			| DropFdwStmt
*************** row_security_cmd:
*** 4708,4713 ****
--- 4710,4764 ----
  
  /*****************************************************************************
   *
+  *		QUERY:
+  *             CREATE ACCESS METHOD name HANDLER handler_name
+  *
+  *****************************************************************************/
+ 
+ CreateAmStmt: CREATE ACCESS METHOD name TYPE_P INDEX HANDLER handler_name
+ 				{
+ 					CreateAmStmt *n = makeNode(CreateAmStmt);
+ 					n->amname = $4;
+ 					n->handler_name = $8;
+ 					n->amtype = 'i';
+ 					$$ = (Node *) n;
+ 				}
+ 		;
+ 
+ /*****************************************************************************
+  *
+  *		QUERY :
+  *				DROP ACCESS METHOD name
+  *
+  ****************************************************************************/
+ 
+ DropAmStmt: DROP ACCESS METHOD name opt_drop_behavior
+ 				{
+ 					DropStmt *n = makeNode(DropStmt);
+ 					n->removeType = OBJECT_ACCESS_METHOD;
+ 					n->objects = list_make1(list_make1(makeString($4)));
+ 					n->arguments = NIL;
+ 					n->missing_ok = false;
+ 					n->behavior = $5;
+ 					n->concurrent = false;
+ 					$$ = (Node *) n;
+ 				}
+ 				|  DROP ACCESS METHOD IF_P EXISTS name opt_drop_behavior
+ 				{
+ 					DropStmt *n = makeNode(DropStmt);
+ 					n->removeType = OBJECT_ACCESS_METHOD;
+ 					n->objects = list_make1(list_make1(makeString($6)));
+ 					n->arguments = NIL;
+ 					n->missing_ok = true;
+ 					n->behavior = $7;
+ 					n->concurrent = false;
+ 					$$ = (Node *) n;
+ 				}
+ 		;
+ 
+ 
+ /*****************************************************************************
+  *
   *		QUERIES :
   *				CREATE TRIGGER ...
   *				DROP TRIGGER ...
*************** unreserved_keyword:
*** 13778,13783 ****
--- 13829,13835 ----
  			| MATCH
  			| MATERIALIZED
  			| MAXVALUE
+ 			| METHOD
  			| MINUTE_P
  			| MINVALUE
  			| MODE
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
new file mode 100644
index dc431c7..d92864c
*** a/src/backend/parser/parse_utilcmd.c
--- b/src/backend/parser/parse_utilcmd.c
*************** transformIndexConstraint(Constraint *con
*** 1709,1715 ****
  		 * else dump and reload will produce a different index (breaking
  		 * pg_upgrade in particular).
  		 */
! 		if (index_rel->rd_rel->relam != get_am_oid(DEFAULT_INDEX_TYPE, false))
  			ereport(ERROR,
  					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
  					 errmsg("index \"%s\" is not a btree", index_name),
--- 1709,1715 ----
  		 * else dump and reload will produce a different index (breaking
  		 * pg_upgrade in particular).
  		 */
! 		if (index_rel->rd_rel->relam != get_am_oid(DEFAULT_INDEX_TYPE, 'i', false))
  			ereport(ERROR,
  					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
  					 errmsg("index \"%s\" is not a btree", index_name),
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
new file mode 100644
index 045f7f0..4d0aac9
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
*************** ProcessUtilitySlow(Node *parsetree,
*** 1520,1525 ****
--- 1520,1529 ----
  				address = ExecSecLabelStmt((SecLabelStmt *) parsetree);
  				break;
  
+ 			case T_CreateAmStmt:
+ 				address = CreateAccessMethod((CreateAmStmt *) parsetree);
+ 				break;
+ 
  			default:
  				elog(ERROR, "unrecognized node type: %d",
  					 (int) nodeTag(parsetree));
*************** CreateCommandTag(Node *parsetree)
*** 2160,2165 ****
--- 2164,2172 ----
  				case OBJECT_TRANSFORM:
  					tag = "DROP TRANSFORM";
  					break;
+ 				case OBJECT_ACCESS_METHOD:
+ 					tag = "DROP ACCESS METHOD";
+ 					break;
  				default:
  					tag = "???";
  			}
*************** CreateCommandTag(Node *parsetree)
*** 2256,2261 ****
--- 2263,2271 ----
  				case OBJECT_COLLATION:
  					tag = "CREATE COLLATION";
  					break;
+ 				case OBJECT_ACCESS_METHOD:
+ 					tag = "CREATE ACCESS METHOD";
+ 					break;
  				default:
  					tag = "???";
  			}
*************** CreateCommandTag(Node *parsetree)
*** 2519,2524 ****
--- 2529,2538 ----
  			tag = "ALTER POLICY";
  			break;
  
+ 		case T_CreateAmStmt:
+ 			tag = "CREATE ACCESS METHOD";
+ 			break;
+ 
  		case T_PrepareStmt:
  			tag = "PREPARE";
  			break;
*************** GetCommandLogLevel(Node *parsetree)
*** 3076,3081 ****
--- 3090,3099 ----
  			lev = LOGSTMT_DDL;
  			break;
  
+ 		case T_CreateAmStmt:
+ 			lev = LOGSTMT_DDL;
+ 			break;
+ 
  			/* already-planned queries */
  		case T_PlannedStmt:
  			{
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
new file mode 100644
index 46c95b0..516af92
*** a/src/backend/utils/adt/selfuncs.c
--- b/src/backend/utils/adt/selfuncs.c
*************** string_to_bytea_const(const char *str, s
*** 6012,6032 ****
   *-------------------------------------------------------------------------
   */
  
! /*
!  * deconstruct_indexquals is a simple function to examine the indexquals
!  * attached to a proposed IndexPath.  It returns a list of IndexQualInfo
!  * structs, one per qual expression.
!  */
! typedef struct
! {
! 	RestrictInfo *rinfo;		/* the indexqual itself */
! 	int			indexcol;		/* zero-based index column number */
! 	bool		varonleft;		/* true if index column is on left of qual */
! 	Oid			clause_op;		/* qual's operator OID, if relevant */
! 	Node	   *other_operand;	/* non-index operand of qual's operator */
! } IndexQualInfo;
! 
! static List *
  deconstruct_indexquals(IndexPath *path)
  {
  	List	   *result = NIL;
--- 6012,6018 ----
   *-------------------------------------------------------------------------
   */
  
! List *
  deconstruct_indexquals(IndexPath *path)
  {
  	List	   *result = NIL;
*************** orderby_operands_eval_cost(PlannerInfo *
*** 6176,6210 ****
  	return qual_arg_cost;
  }
  
! /*
!  * genericcostestimate is a general-purpose estimator that can be used for
!  * most index types.  In some cases we use genericcostestimate as the base
!  * code and then incorporate additional index-type-specific knowledge in
!  * the type-specific calling function.  To avoid code duplication, we make
!  * genericcostestimate return a number of intermediate values as well as
!  * its preliminary estimates of the output cost values.  The GenericCosts
!  * struct includes all these values.
!  *
!  * Callers should initialize all fields of GenericCosts to zero.  In addition,
!  * they can set numIndexTuples to some positive value if they have a better
!  * than default way of estimating the number of leaf index tuples visited.
!  */
! typedef struct
! {
! 	/* These are the values the cost estimator must return to the planner */
! 	Cost		indexStartupCost;		/* index-related startup cost */
! 	Cost		indexTotalCost; /* total index-related scan cost */
! 	Selectivity indexSelectivity;		/* selectivity of index */
! 	double		indexCorrelation;		/* order correlation of index */
! 
! 	/* Intermediate values we obtain along the way */
! 	double		numIndexPages;	/* number of leaf pages visited */
! 	double		numIndexTuples; /* number of leaf tuples visited */
! 	double		spc_random_page_cost;	/* relevant random_page_cost value */
! 	double		num_sa_scans;	/* # indexscans from ScalarArrayOps */
! } GenericCosts;
! 
! static void
  genericcostestimate(PlannerInfo *root,
  					IndexPath *path,
  					double loop_count,
--- 6162,6168 ----
  	return qual_arg_cost;
  }
  
! void
  genericcostestimate(PlannerInfo *root,
  					IndexPath *path,
  					double loop_count,
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
new file mode 100644
index f798b15..1acd91a
*** a/src/bin/pg_dump/common.c
--- b/src/bin/pg_dump/common.c
*************** getSchemaData(Archive *fout, int *numTab
*** 98,103 ****
--- 98,104 ----
  	int			numProcLangs;
  	int			numCasts;
  	int			numTransforms;
+ 	int			numAccessMethods;
  	int			numOpclasses;
  	int			numOpfamilies;
  	int			numConversions;
*************** getSchemaData(Archive *fout, int *numTab
*** 169,174 ****
--- 170,179 ----
  	oprinfoindex = buildIndexArray(oprinfo, numOperators, sizeof(OprInfo));
  
  	if (g_verbose)
+ 		write_msg(NULL, "reading user-defined access methods\n");
+ 	getAccessMethods(fout, &numAccessMethods);
+ 
+ 	if (g_verbose)
  		write_msg(NULL, "reading user-defined operator classes\n");
  	getOpclasses(fout, &numOpclasses);
  
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
new file mode 100644
index 64c2673..8999bcc
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
*************** static void dumpFunc(Archive *fout, Func
*** 173,178 ****
--- 173,179 ----
  static void dumpCast(Archive *fout, CastInfo *cast);
  static void dumpTransform(Archive *fout, TransformInfo *transform);
  static void dumpOpr(Archive *fout, OprInfo *oprinfo);
+ static void dumpAccessMethod(Archive *fout, AccessMethodInfo *oprinfo);
  static void dumpOpclass(Archive *fout, OpclassInfo *opcinfo);
  static void dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo);
  static void dumpCollation(Archive *fout, CollInfo *convinfo);
*************** getConversions(Archive *fout, int *numCo
*** 4101,4106 ****
--- 4102,4183 ----
  }
  
  /*
+  * getAccessMethods:
+  *	  read all user-defined access methods in the system catalogs and return
+  *    them in the AccessMethodInfo* structure
+  *
+  *	numAccessMethods is set to the number of access methods read in
+  */
+ AccessMethodInfo *
+ getAccessMethods(Archive *fout, int *numAccessMethods)
+ {
+ 	DumpOptions *dopt = fout->dopt;
+ 	PGresult   *res;
+ 	int			ntups;
+ 	int			i;
+ 	PQExpBuffer query;
+ 	AccessMethodInfo *aminfo;
+ 	int			i_tableoid;
+ 	int			i_oid;
+ 	int			i_amname;
+ 	int			i_amhandler;
+ 
+ 	/* Before 9.6, there are no user-defined access methods */
+ 	if (fout->remoteVersion < 90600)
+ 	{
+ 		*numAccessMethods = 0;
+ 		return NULL;
+ 	}
+ 
+ 	query = createPQExpBuffer();
+ 
+ 	/* Make sure we are in proper schema */
+ 	selectSourceSchema(fout, "pg_catalog");
+ 
+ 	/*
+ 	 * Select only user-defined access methods assuming all built-in access
+ 	 * methods have oid < 10000.
+ 	 */
+ 	appendPQExpBuffer(query, "SELECT tableoid, oid, amname, "
+ 					  "amhandler::pg_catalog.regproc AS amhandler "
+ 					  "FROM pg_am "
+ 					  "WHERE oid >= 10000");
+ 
+ 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+ 
+ 	ntups = PQntuples(res);
+ 	*numAccessMethods = ntups;
+ 
+ 	aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
+ 
+ 	i_tableoid = PQfnumber(res, "tableoid");
+ 	i_oid = PQfnumber(res, "oid");
+ 	i_amname = PQfnumber(res, "amname");
+ 	i_amhandler = PQfnumber(res, "amhandler");
+ 
+ 	for (i = 0; i < ntups; i++)
+ 	{
+ 		aminfo[i].dobj.objType = DO_ACCESS_METHOD;
+ 		aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ 		aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ 		AssignDumpId(&aminfo[i].dobj);
+ 		aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
+ 		aminfo[i].dobj.namespace = NULL;
+ 		aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
+ 
+ 		/* Decide whether we want to dump it */
+ 		selectDumpableObject(&(aminfo[i].dobj), dopt);
+ 	}
+ 
+ 	PQclear(res);
+ 
+ 	destroyPQExpBuffer(query);
+ 
+ 	return aminfo;
+ }
+ 
+ 
+ /*
   * getOpclasses:
   *	  read all opclasses in the system catalogs and return them in the
   * OpclassInfo* structure
*************** dumpDumpableObject(Archive *fout, Dumpab
*** 8408,8413 ****
--- 8485,8493 ----
  		case DO_OPERATOR:
  			dumpOpr(fout, (OprInfo *) dobj);
  			break;
+ 		case DO_ACCESS_METHOD:
+ 			dumpAccessMethod(fout, (AccessMethodInfo *) dobj);
+ 			break;
  		case DO_OPCLASS:
  			dumpOpclass(fout, (OpclassInfo *) dobj);
  			break;
*************** convertTSFunction(Archive *fout, Oid fun
*** 11446,11451 ****
--- 11526,11584 ----
  	return result;
  }
  
+ /*
+  * dumpAccessMethod
+  *	  write out a single access method definition
+  */
+ static void
+ dumpAccessMethod(Archive *fout, AccessMethodInfo *aminfo)
+ {
+ 	DumpOptions *dopt = fout->dopt;
+ 	PQExpBuffer q;
+ 	PQExpBuffer delq;
+ 	PQExpBuffer labelq;
+ 	char	   *qamname;
+ 
+ 	/* Skip if not to be dumped */
+ 	if (!aminfo->dobj.dump || dopt->dataOnly)
+ 		return;
+ 
+ 	q = createPQExpBuffer();
+ 	delq = createPQExpBuffer();
+ 	labelq = createPQExpBuffer();
+ 
+ 	qamname = pg_strdup(fmtId(aminfo->dobj.name));
+ 
+ 	appendPQExpBuffer(q, "CREATE ACCESS METHOD %s HANDLER %s;\n",
+ 					  qamname, aminfo->amhandler);
+ 
+ 	appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
+ 					  qamname);
+ 
+ 	appendPQExpBuffer(labelq, "ACCESS METHOD %s",
+ 					  qamname);
+ 
+ 	ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
+ 				 aminfo->dobj.name,
+ 				 NULL,
+ 				 NULL,
+ 				 "",
+ 				 false, "ACCESS METHOD", SECTION_PRE_DATA,
+ 				 q->data, delq->data, NULL,
+ 				 NULL, 0,
+ 				 NULL, NULL);
+ 
+ 	/* Dump Access Method Comments */
+ 	dumpComment(fout, labelq->data,
+ 				NULL, "",
+ 				aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
+ 
+ 	free(qamname);
+ 
+ 	destroyPQExpBuffer(q);
+ 	destroyPQExpBuffer(delq);
+ 	destroyPQExpBuffer(labelq);
+ }
  
  /*
   * dumpOpclass
*************** addBoundaryDependencies(DumpableObject *
*** 16227,16232 ****
--- 16360,16366 ----
  			case DO_FUNC:
  			case DO_AGG:
  			case DO_OPERATOR:
+ 			case DO_ACCESS_METHOD:
  			case DO_OPCLASS:
  			case DO_OPFAMILY:
  			case DO_COLLATION:
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
new file mode 100644
index 9a1d8f8..ad37047
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
*************** typedef enum
*** 48,53 ****
--- 48,54 ----
  	DO_FUNC,
  	DO_AGG,
  	DO_OPERATOR,
+ 	DO_ACCESS_METHOD,
  	DO_OPCLASS,
  	DO_OPFAMILY,
  	DO_COLLATION,
*************** typedef enum
*** 77,83 ****
  	DO_POST_DATA_BOUNDARY,
  	DO_EVENT_TRIGGER,
  	DO_REFRESH_MATVIEW,
! 	DO_POLICY
  } DumpableObjectType;
  
  typedef struct _dumpableObject
--- 78,84 ----
  	DO_POST_DATA_BOUNDARY,
  	DO_EVENT_TRIGGER,
  	DO_REFRESH_MATVIEW,
! 	DO_POLICY,
  } DumpableObjectType;
  
  typedef struct _dumpableObject
*************** typedef struct _oprInfo
*** 167,172 ****
--- 168,179 ----
  	Oid			oprcode;
  } OprInfo;
  
+ typedef struct _accessMethodInfo
+ {
+ 	DumpableObject dobj;
+ 	char	   *amhandler;
+ } AccessMethodInfo;
+ 
  typedef struct _opclassInfo
  {
  	DumpableObject dobj;
*************** extern TypeInfo *getTypes(Archive *fout,
*** 548,553 ****
--- 555,561 ----
  extern FuncInfo *getFuncs(Archive *fout, int *numFuncs);
  extern AggInfo *getAggregates(Archive *fout, int *numAggregates);
  extern OprInfo *getOperators(Archive *fout, int *numOperators);
+ extern AccessMethodInfo *getAccessMethods(Archive *fout, int *numAccessMethods);
  extern OpclassInfo *getOpclasses(Archive *fout, int *numOpclasses);
  extern OpfamilyInfo *getOpfamilies(Archive *fout, int *numOpfamilies);
  extern CollInfo *getCollations(Archive *fout, int *numCollations);
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
new file mode 100644
index 78ff59c..3bd5bd8
*** a/src/bin/pg_dump/pg_dump_sort.c
--- b/src/bin/pg_dump/pg_dump_sort.c
*************** static const int oldObjectTypePriority[]
*** 45,50 ****
--- 45,51 ----
  	2,							/* DO_FUNC */
  	3,							/* DO_AGG */
  	3,							/* DO_OPERATOR */
+ 	3,							/* DO_ACCESS_METHOD */
  	4,							/* DO_OPCLASS */
  	4,							/* DO_OPFAMILY */
  	4,							/* DO_COLLATION */
*************** static const int newObjectTypePriority[]
*** 95,100 ****
--- 96,102 ----
  	6,							/* DO_FUNC */
  	7,							/* DO_AGG */
  	8,							/* DO_OPERATOR */
+ 	8,							/* DO_ACCESS_METHOD */
  	9,							/* DO_OPCLASS */
  	9,							/* DO_OPFAMILY */
  	3,							/* DO_COLLATION */
*************** describeDumpableObject(DumpableObject *o
*** 1329,1334 ****
--- 1331,1340 ----
  					 "OPERATOR %s  (ID %d OID %u)",
  					 obj->name, obj->dumpId, obj->catId.oid);
  			return;
+ 		case DO_ACCESS_METHOD:
+ 			snprintf(buf, bufsize,
+ 					 "");
+ 			return;
  		case DO_OPCLASS:
  			snprintf(buf, bufsize,
  					 "OPERATOR CLASS %s  (ID %d OID %u)",
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
new file mode 100644
index 049bf9f..ac16740
*** a/src/include/catalog/dependency.h
--- b/src/include/catalog/dependency.h
*************** typedef enum ObjectClass
*** 153,162 ****
  	OCLASS_EXTENSION,			/* pg_extension */
  	OCLASS_EVENT_TRIGGER,		/* pg_event_trigger */
  	OCLASS_POLICY,				/* pg_policy */
! 	OCLASS_TRANSFORM			/* pg_transform */
  } ObjectClass;
  
! #define LAST_OCLASS		OCLASS_TRANSFORM
  
  
  /* in dependency.c */
--- 153,163 ----
  	OCLASS_EXTENSION,			/* pg_extension */
  	OCLASS_EVENT_TRIGGER,		/* pg_event_trigger */
  	OCLASS_POLICY,				/* pg_policy */
! 	OCLASS_TRANSFORM,			/* pg_transform */
! 	OCLASS_AM,					/* pg_am */
  } ObjectClass;
  
! #define LAST_OCLASS		OCLASS_AM
  
  
  /* in dependency.c */
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
new file mode 100644
index f801c3e..605e154
*** a/src/include/catalog/pg_am.h
--- b/src/include/catalog/pg_am.h
*************** CATALOG(pg_am,2601)
*** 35,40 ****
--- 35,47 ----
  {
  	NameData	amname;			/* access method name */
  	regproc		amhandler;		/* handler function */
+ 
+ 	/*----------
+ 	 * Type of access method. Possible values are
+ 	 *		'i': Index access method
+ 	 *----------
+ 	 */
+ 	char		amtype;
  } FormData_pg_am;
  
  /* ----------------
*************** typedef FormData_pg_am *Form_pg_am;
*** 48,78 ****
   *		compiler constants for pg_am
   * ----------------
   */
! #define Natts_pg_am						2
  #define Anum_pg_am_amname				1
  #define Anum_pg_am_amhandler			2
  
  /* ----------------
   *		initial contents of pg_am
   * ----------------
   */
  
! DATA(insert OID = 403 (  btree		bthandler ));
  DESCR("b-tree index access method");
  #define BTREE_AM_OID 403
! DATA(insert OID = 405 (  hash		hashhandler ));
  DESCR("hash index access method");
  #define HASH_AM_OID 405
! DATA(insert OID = 783 (  gist		gisthandler ));
  DESCR("GiST index access method");
  #define GIST_AM_OID 783
! DATA(insert OID = 2742 (  gin		ginhandler ));
  DESCR("GIN index access method");
  #define GIN_AM_OID 2742
! DATA(insert OID = 4000 (  spgist	spghandler ));
  DESCR("SP-GiST index access method");
  #define SPGIST_AM_OID 4000
! DATA(insert OID = 3580 (  brin		brinhandler ));
  DESCR("block range index (BRIN) access method");
  #define BRIN_AM_OID 3580
  
--- 55,86 ----
   *		compiler constants for pg_am
   * ----------------
   */
! #define Natts_pg_am						3
  #define Anum_pg_am_amname				1
  #define Anum_pg_am_amhandler			2
+ #define Anum_pg_am_amtype				3
  
  /* ----------------
   *		initial contents of pg_am
   * ----------------
   */
  
! DATA(insert OID = 403 (  btree		bthandler	i ));
  DESCR("b-tree index access method");
  #define BTREE_AM_OID 403
! DATA(insert OID = 405 (  hash		hashhandler	i ));
  DESCR("hash index access method");
  #define HASH_AM_OID 405
! DATA(insert OID = 783 (  gist		gisthandler	i ));
  DESCR("GiST index access method");
  #define GIST_AM_OID 783
! DATA(insert OID = 2742 (  gin		ginhandler	i ));
  DESCR("GIN index access method");
  #define GIN_AM_OID 2742
! DATA(insert OID = 4000 (  spgist	spghandler	i ));
  DESCR("SP-GiST index access method");
  #define SPGIST_AM_OID 4000
! DATA(insert OID = 3580 (  brin		brinhandler	i ));
  DESCR("block range index (BRIN) access method");
  #define BRIN_AM_OID 3580
  
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
new file mode 100644
index 54f67e9..6df15ed
*** a/src/include/commands/defrem.h
--- b/src/include/commands/defrem.h
*************** extern void IsThereOpClassInNamespace(co
*** 91,98 ****
  						  Oid opcnamespace);
  extern void IsThereOpFamilyInNamespace(const char *opfname, Oid opfmethod,
  						   Oid opfnamespace);
- extern Oid	get_am_oid(const char *amname, bool missing_ok);
- extern char *get_am_name(Oid amOid);
  extern Oid	get_opclass_oid(Oid amID, List *opclassname, bool missing_ok);
  extern Oid	get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok);
  
--- 91,96 ----
*************** extern Datum transformGenericOptions(Oid
*** 137,142 ****
--- 135,146 ----
  						List *options,
  						Oid fdwvalidator);
  
+ /* commands/amcmds.c */
+ extern ObjectAddress CreateAccessMethod(CreateAmStmt *stmt);
+ extern void RemoveAccessMethodById(Oid amOid);
+ extern Oid	get_am_oid(const char *amname, char amtype, bool missing_ok);
+ extern char *get_am_name(Oid amOid, char amtype);
+ 
  /* support routines in commands/define.c */
  
  extern char *defGetString(DefElem *def);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
new file mode 100644
index c407fa2..7d46763
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
*************** typedef enum NodeTag
*** 386,391 ****
--- 386,392 ----
  	T_CreatePolicyStmt,
  	T_AlterPolicyStmt,
  	T_CreateTransformStmt,
+ 	T_CreateAmStmt,
  
  	/*
  	 * TAGS FOR PARSE TREE NODES (parsenodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
new file mode 100644
index 2fd0629..8b958b4
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
*************** typedef struct SetOperationStmt
*** 1379,1384 ****
--- 1379,1385 ----
  
  typedef enum ObjectType
  {
+ 	OBJECT_ACCESS_METHOD,
  	OBJECT_AGGREGATE,
  	OBJECT_AMOP,
  	OBJECT_AMPROC,
*************** typedef struct AlterPolicyStmt
*** 2070,2075 ****
--- 2071,2088 ----
  	Node	   *with_check;		/* the policy's WITH CHECK condition. */
  } AlterPolicyStmt;
  
+ /*----------------------
+  *		Create ACCESS METHOD Statement
+  *----------------------
+  */
+ typedef struct CreateAmStmt
+ {
+ 	NodeTag		type;
+ 	char	   *amname;			/* access method name */
+ 	List	   *handler_name;	/* handler function name */
+ 	char		amtype;			/* type of access method */
+ } CreateAmStmt;
+ 
  /* ----------------------
   *		Create TRIGGER Statement
   * ----------------------
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
new file mode 100644
index 6e1e820..7de3404
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
*************** PG_KEYWORD("mapping", MAPPING, UNRESERVE
*** 239,244 ****
--- 239,245 ----
  PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD)
  PG_KEYWORD("materialized", MATERIALIZED, UNRESERVED_KEYWORD)
  PG_KEYWORD("maxvalue", MAXVALUE, UNRESERVED_KEYWORD)
+ PG_KEYWORD("method", METHOD, UNRESERVED_KEYWORD)
  PG_KEYWORD("minute", MINUTE_P, UNRESERVED_KEYWORD)
  PG_KEYWORD("minvalue", MINVALUE, UNRESERVED_KEYWORD)
  PG_KEYWORD("mode", MODE, UNRESERVED_KEYWORD)
diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h
new file mode 100644
index 06fbca7..7fb7466
*** a/src/include/utils/selfuncs.h
--- b/src/include/utils/selfuncs.h
*************** typedef enum
*** 95,100 ****
--- 95,142 ----
  	Pattern_Prefix_None, Pattern_Prefix_Partial, Pattern_Prefix_Exact
  } Pattern_Prefix_Status;
  
+ /*
+  * deconstruct_indexquals is a simple function to examine the indexquals
+  * attached to a proposed IndexPath.  It returns a list of IndexQualInfo
+  * structs, one per qual expression.
+  */
+ typedef struct
+ {
+ 	RestrictInfo *rinfo;		/* the indexqual itself */
+ 	int			indexcol;		/* zero-based index column number */
+ 	bool		varonleft;		/* true if index column is on left of qual */
+ 	Oid			clause_op;		/* qual's operator OID, if relevant */
+ 	Node	   *other_operand;	/* non-index operand of qual's operator */
+ } IndexQualInfo;
+ 
+ /*
+  * genericcostestimate is a general-purpose estimator that can be used for
+  * most index types.  In some cases we use genericcostestimate as the base
+  * code and then incorporate additional index-type-specific knowledge in
+  * the type-specific calling function.  To avoid code duplication, we make
+  * genericcostestimate return a number of intermediate values as well as
+  * its preliminary estimates of the output cost values.  The GenericCosts
+  * struct includes all these values.
+  *
+  * Callers should initialize all fields of GenericCosts to zero.  In addition,
+  * they can set numIndexTuples to some positive value if they have a better
+  * than default way of estimating the number of leaf index tuples visited.
+  */
+ typedef struct
+ {
+ 	/* These are the values the cost estimator must return to the planner */
+ 	Cost		indexStartupCost;		/* index-related startup cost */
+ 	Cost		indexTotalCost; /* total index-related scan cost */
+ 	Selectivity indexSelectivity;		/* selectivity of index */
+ 	double		indexCorrelation;		/* order correlation of index */
+ 
+ 	/* Intermediate values we obtain along the way */
+ 	double		numIndexPages;	/* number of leaf pages visited */
+ 	double		numIndexTuples; /* number of leaf tuples visited */
+ 	double		spc_random_page_cost;	/* relevant random_page_cost value */
+ 	double		num_sa_scans;	/* # indexscans from ScalarArrayOps */
+ } GenericCosts;
+ 
  /* Hooks for plugins to get control when we ask for stats */
  typedef bool (*get_relation_stats_hook_type) (PlannerInfo *root,
  														  RangeTblEntry *rte,
*************** extern double estimate_num_groups(Planne
*** 191,196 ****
--- 233,244 ----
  extern Selectivity estimate_hash_bucketsize(PlannerInfo *root, Node *hashkey,
  						 double nbuckets);
  
+ extern List *deconstruct_indexquals(IndexPath *path);
+ extern void genericcostestimate(PlannerInfo *root, IndexPath *path,
+ 								double loop_count,
+ 								List *qinfos,
+ 								GenericCosts *costs);
+ 
  /* Functions in array_selfuncs.c */
  
  extern Selectivity scalararraysel_containment(PlannerInfo *root,
diff --git a/src/test/regress/expected/create_am.out b/src/test/regress/expected/create_am.out
new file mode 100644
index ...47d6024
*** a/src/test/regress/expected/create_am.out
--- b/src/test/regress/expected/create_am.out
***************
*** 0 ****
--- 1,108 ----
+ --
+ -- Create access method tests
+ --
+ -- Make gist2 over gisthandler. In fact, it would be a synonym to gist.
+ CREATE ACCESS METHOD gist2 TYPE INDEX HANDLER gisthandler;
+ -- Drop old index on fast_emp4000
+ DROP INDEX grect2ind;
+ -- Try to create gist2 index on fast_emp4000: fail because opclass doesn't exist
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ ERROR:  data type box has no default operator class for access method "gist2"
+ HINT:  You must specify an operator class for the index or define a default operator class for the data type.
+ -- Make operator class for boxes using gist2
+ CREATE OPERATOR CLASS box_ops DEFAULT
+ 	FOR TYPE box USING gist2 AS
+ 	OPERATOR 1	<<,
+ 	OPERATOR 2	&<,
+ 	OPERATOR 3	&&,
+ 	OPERATOR 4	&>,
+ 	OPERATOR 5	>>,
+ 	OPERATOR 6	~=,
+ 	OPERATOR 7	@>,
+ 	OPERATOR 8	<@,
+ 	OPERATOR 9	&<|,
+ 	OPERATOR 10	<<|,
+ 	OPERATOR 11	|>>,
+ 	OPERATOR 12	|&>,
+ 	OPERATOR 13	~,
+ 	OPERATOR 14	@,
+ 	FUNCTION 1	gist_box_consistent(internal, box, smallint, oid, internal),
+ 	FUNCTION 2	gist_box_union(internal, internal),
+ 	FUNCTION 3	gist_box_compress(internal),
+ 	FUNCTION 4	gist_box_decompress(internal),
+ 	FUNCTION 5	gist_box_penalty(internal, internal, internal),
+ 	FUNCTION 6	gist_box_picksplit(internal, internal),
+ 	FUNCTION 7	gist_box_same(box, box, internal),
+ 	FUNCTION 9	gist_box_fetch(internal);
+ -- Create gist2 index on fast_emp4000
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ -- Now check the results from plain indexscan
+ SET enable_seqscan = OFF;
+ SET enable_indexscan = ON;
+ SET enable_bitmapscan = OFF;
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+                            QUERY PLAN                           
+ ----------------------------------------------------------------
+  Sort
+    Sort Key: ((home_base[0])[0])
+    ->  Index Only Scan using grect2ind on fast_emp4000
+          Index Cond: (home_base @ '(2000,1000),(200,200)'::box)
+ (4 rows)
+ 
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+        home_base       
+ -----------------------
+  (337,455),(240,359)
+  (1444,403),(1346,344)
+ (2 rows)
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+                          QUERY PLAN                          
+ -------------------------------------------------------------
+  Aggregate
+    ->  Index Only Scan using grect2ind on fast_emp4000
+          Index Cond: (home_base && '(1000,1000),(0,0)'::box)
+ (3 rows)
+ 
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+  count 
+ -------
+      2
+ (1 row)
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+                       QUERY PLAN                       
+ -------------------------------------------------------
+  Aggregate
+    ->  Index Only Scan using grect2ind on fast_emp4000
+          Index Cond: (home_base IS NULL)
+ (3 rows)
+ 
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+  count 
+ -------
+    278
+ (1 row)
+ 
+ -- Try to drop access method: fail because of depending objects
+ DROP ACCESS METHOD gist2;
+ ERROR:  cannot drop access method gist2 because other objects depend on it
+ DETAIL:  operator class box_ops for access method gist2 depends on access method gist2
+ index grect2ind depends on operator class box_ops for access method gist2
+ HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+ -- Drop access method cascade
+ DROP ACCESS METHOD gist2 CASCADE;
+ NOTICE:  drop cascades to 2 other objects
+ DETAIL:  drop cascades to operator class box_ops for access method gist2
+ drop cascades to index grect2ind
+ -- Reset optimizer options
+ RESET enable_seqscan;
+ RESET enable_indexscan;
+ RESET enable_bitmapscan;
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
new file mode 100644
index eb0bc88..2c5be4b
*** a/src/test/regress/expected/sanity_check.out
--- b/src/test/regress/expected/sanity_check.out
*************** e_star|f
*** 44,50 ****
  emp|f
  equipment_r|f
  f_star|f
! fast_emp4000|t
  float4_tbl|f
  float8_tbl|f
  func_index_heap|t
--- 44,50 ----
  emp|f
  equipment_r|f
  f_star|f
! fast_emp4000|f
  float4_tbl|f
  float8_tbl|f
  func_index_heap|t
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
new file mode 100644
index bec0316..8be4b83
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
*************** test: create_index create_view
*** 60,66 ****
  # ----------
  # Another group of parallel tests
  # ----------
! test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes
  
  # ----------
  # sanity_check does a vacuum, affecting the sort order of SELECT *
--- 60,66 ----
  # ----------
  # Another group of parallel tests
  # ----------
! test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes create_am
  
  # ----------
  # sanity_check does a vacuum, affecting the sort order of SELECT *
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
new file mode 100644
index 7e9b319..1de3da8
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
*************** test: drop_if_exists
*** 75,80 ****
--- 75,81 ----
  test: updatable_views
  test: rolenames
  test: roleattributes
+ test: create_am
  test: sanity_check
  test: errors
  test: select
diff --git a/src/test/regress/sql/create_am.sql b/src/test/regress/sql/create_am.sql
new file mode 100644
index ...e2051c5
*** a/src/test/regress/sql/create_am.sql
--- b/src/test/regress/sql/create_am.sql
***************
*** 0 ****
--- 1,73 ----
+ --
+ -- Create access method tests
+ --
+ 
+ -- Make gist2 over gisthandler. In fact, it would be a synonym to gist.
+ CREATE ACCESS METHOD gist2 TYPE INDEX HANDLER gisthandler;
+ 
+ -- Drop old index on fast_emp4000
+ DROP INDEX grect2ind;
+ 
+ -- Try to create gist2 index on fast_emp4000: fail because opclass doesn't exist
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ 
+ -- Make operator class for boxes using gist2
+ CREATE OPERATOR CLASS box_ops DEFAULT
+ 	FOR TYPE box USING gist2 AS
+ 	OPERATOR 1	<<,
+ 	OPERATOR 2	&<,
+ 	OPERATOR 3	&&,
+ 	OPERATOR 4	&>,
+ 	OPERATOR 5	>>,
+ 	OPERATOR 6	~=,
+ 	OPERATOR 7	@>,
+ 	OPERATOR 8	<@,
+ 	OPERATOR 9	&<|,
+ 	OPERATOR 10	<<|,
+ 	OPERATOR 11	|>>,
+ 	OPERATOR 12	|&>,
+ 	OPERATOR 13	~,
+ 	OPERATOR 14	@,
+ 	FUNCTION 1	gist_box_consistent(internal, box, smallint, oid, internal),
+ 	FUNCTION 2	gist_box_union(internal, internal),
+ 	FUNCTION 3	gist_box_compress(internal),
+ 	FUNCTION 4	gist_box_decompress(internal),
+ 	FUNCTION 5	gist_box_penalty(internal, internal, internal),
+ 	FUNCTION 6	gist_box_picksplit(internal, internal),
+ 	FUNCTION 7	gist_box_same(box, box, internal),
+ 	FUNCTION 9	gist_box_fetch(internal);
+ 
+ -- Create gist2 index on fast_emp4000
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ 
+ -- Now check the results from plain indexscan
+ SET enable_seqscan = OFF;
+ SET enable_indexscan = ON;
+ SET enable_bitmapscan = OFF;
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+ 
+ -- Try to drop access method: fail because of depending objects
+ DROP ACCESS METHOD gist2;
+ 
+ -- Drop access method cascade
+ DROP ACCESS METHOD gist2 CASCADE;
+ 
+ -- Reset optimizer options
+ RESET enable_seqscan;
+ RESET enable_indexscan;
+ RESET enable_bitmapscan;
generic-xlog.7.patchapplication/octet-stream; name=generic-xlog.7.patchDownload
diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
new file mode 100644
index c72a1f2..c0e38fd
*** a/src/backend/access/rmgrdesc/Makefile
--- b/src/backend/access/rmgrdesc/Makefile
*************** subdir = src/backend/access/rmgrdesc
*** 8,16 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o gindesc.o gistdesc.o \
! 	   hashdesc.o heapdesc.o mxactdesc.o nbtdesc.o relmapdesc.o \
! 	   replorigindesc.o seqdesc.o smgrdesc.o spgdesc.o \
  	   standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
  
  include $(top_srcdir)/src/backend/common.mk
--- 8,16 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o genericdesc.o \
! 	   gindesc.o gistdesc.o hashdesc.o heapdesc.o mxactdesc.o nbtdesc.o \
! 	   relmapdesc.o replorigindesc.o seqdesc.o smgrdesc.o spgdesc.o \
  	   standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
  
  include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/genericdesc.c b/src/backend/access/rmgrdesc/genericdesc.c
new file mode 100644
index ...3d035c2
*** a/src/backend/access/rmgrdesc/genericdesc.c
--- b/src/backend/access/rmgrdesc/genericdesc.c
***************
*** 0 ****
--- 1,58 ----
+ /*-------------------------------------------------------------------------
+  *
+  * genericdesc.c
+  *	  rmgr descriptor routines for access/transam/generic_xlog.c
+  *
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/backend/access/rmgrdesc/genericdesc.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/generic_xlog.h"
+ #include "lib/stringinfo.h"
+ #include "storage/relfilenode.h"
+ 
+ /*
+  * Description of generic xlog record: write page regions which this record
+  * overrides.
+  */
+ void
+ generic_desc(StringInfo buf, XLogReaderState *record)
+ {
+ 	Pointer		ptr = XLogRecGetData(record),
+ 				end = ptr + XLogRecGetDataLen(record);
+ 
+ 	while (ptr < end)
+ 	{
+ 		OffsetNumber	offset,
+ 						length;
+ 
+ 		memcpy(&offset, ptr, sizeof(offset));
+ 		ptr += sizeof(offset);
+ 		memcpy(&length, ptr, sizeof(length));
+ 		ptr += sizeof(length);
+ 		ptr += length;
+ 
+ 		if (ptr < end)
+ 			appendStringInfo(buf, "offset %u, length %u; ", offset, length);
+ 		else
+ 			appendStringInfo(buf, "offset %u, length %u", offset, length);
+ 	}
+ 
+ 	return;
+ }
+ 
+ /*
+  * Identification of generic xlog record: we don't distinguish any subtypes
+  * inside generic xlog records.
+  */
+ const char *
+ generic_identify(uint8 info)
+ {
+ 	return "Generic";
+ }
diff --git a/src/backend/access/transam/Makefile b/src/backend/access/transam/Makefile
new file mode 100644
index 94455b2..16fbe47
*** a/src/backend/access/transam/Makefile
--- b/src/backend/access/transam/Makefile
*************** subdir = src/backend/access/transam
*** 12,19 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = clog.o commit_ts.o multixact.o parallel.o rmgr.o slru.o subtrans.o \
! 	timeline.o transam.o twophase.o twophase_rmgr.o varsup.o \
  	xact.o xlog.o xlogarchive.o xlogfuncs.o \
  	xloginsert.o xlogreader.o xlogutils.o
  
--- 12,19 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = clog.o commit_ts.o generic_xlog.o multixact.o parallel.o rmgr.o slru.o \
! 	subtrans.o timeline.o transam.o twophase.o twophase_rmgr.o varsup.o \
  	xact.o xlog.o xlogarchive.o xlogfuncs.o \
  	xloginsert.o xlogreader.o xlogutils.o
  
diff --git a/src/backend/access/transam/generic_xlog.c b/src/backend/access/transam/generic_xlog.c
new file mode 100644
index ...2dcba91
*** a/src/backend/access/transam/generic_xlog.c
--- b/src/backend/access/transam/generic_xlog.c
***************
*** 0 ****
--- 1,435 ----
+ /*-------------------------------------------------------------------------
+  *
+  * generic_xlog.c
+  *	 Implementation of generic xlog records.
+  *
+  *
+  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/backend/access/transam/generic_xlog.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/generic_xlog.h"
+ #include "access/xlogutils.h"
+ #include "miscadmin.h"
+ #include "utils/memutils.h"
+ 
+ #define MATCH_THRESHOLD			  4
+ 
+ /* Struct of generic xlog data for single page */
+ typedef struct
+ {
+ 	Buffer	buffer;			/* registered buffer */
+ 	char	image[BLCKSZ];	/* copy of page image for modification */
+ 	char	data[BLCKSZ + 2 * sizeof(OffsetNumber)]; /* delta between page images */
+ 	int		dataLen;		/* space consumed in data field */
+ 	bool	fullImage;		/* are we taking full image of this page? */
+ } PageData;
+ 
+ /* Enum of generic xlog (gxlog) status */
+ enum GenericXlogStatus
+ {
+ 	GXLOG_NOT_STARTED,	/* gxlog is not started */
+ 	GXLOG_LOGGED,		/* gxlog is started for logged relation */
+ 	GXLOG_UNLOGGED		/* gxlog is started for unlogged relation */
+ };
+ 
+ static enum GenericXlogStatus	genericXlogStatus = GXLOG_NOT_STARTED;
+ static PageData					pages[MAX_GENERIC_XLOG_PAGES];
+ 
+ 
+ static void writeFragment(PageData *pageData, OffsetNumber offset,
+ 						  OffsetNumber len, Pointer data);
+ static void writeDelta(PageData *pageData);
+ static void applyPageRedo(Page page, Pointer data, Size dataSize);
+ 
+ /*---
+  * Write next fragment into delta.  Each fragment represents changes maded in
+  * given region of page.  Fragment is described as following.
+  *
+  * - offset of page region (OffsetNumber)
+  * - length of page region (OffsetNumber)
+  * - data - the data to place into described region ('length' number of bytes)
+  */
+ static void
+ writeFragment(PageData *pageData, OffsetNumber offset, OffsetNumber length,
+ 			  Pointer data)
+ {
+ 	Pointer			ptr = pageData->data + pageData->dataLen;
+ 
+ 	/* Check we have enough of space */
+ 	Assert(pageData->dataLen + sizeof(offset) +
+ 		   sizeof(length) + length <= sizeof(pageData->data));
+ 
+ 	/* Write fragment data */
+ 	memcpy(ptr, &offset, sizeof(offset));
+ 	ptr += sizeof(offset);
+ 	memcpy(ptr, &length, sizeof(length));
+ 	ptr += sizeof(length);
+ 	memcpy(ptr, data, length);
+ 	ptr += length;
+ 
+ 	pageData->dataLen = ptr - pageData->data;
+ }
+ 
+ /*
+  * Make delta for given page.
+  */
+ static void
+ writeDelta(PageData *pageData)
+ {
+ 	Page			page = BufferGetPage(pageData->buffer),
+ 					image = (Page) pageData->image;
+ 	int				i,
+ 					fragmentBegin = -1,
+ 					fragmentEnd = -1;
+ 	uint16			pageLower = ((PageHeader) page)->pd_lower,
+ 					pageUpper = ((PageHeader) page)->pd_upper,
+ 					imageLower = ((PageHeader) image)->pd_lower,
+ 					imageUpper = ((PageHeader) image)->pd_upper;
+ 
+ 	for (i = 0; i < BLCKSZ; i++)
+ 	{
+ 		bool	match;
+ 
+ 		/*
+ 		 * Check if bytes in old and new page images matches.  We don't rely
+ 		 * data in unallocated area between pd_lower and pd_upper.  Thus we
+ 		 * assume unallocated area to expand with unmatched bytes.  Bytes
+ 		 * inside unallocated area are assumed to always match.
+ 		 */
+ 		if (i < pageLower)
+ 		{
+ 			if (i < imageLower)
+ 				match = (page[i] == image[i]);
+ 			else
+ 				match = false;
+ 		}
+ 		else if (i >= pageUpper)
+ 		{
+ 			if (i >= imageUpper)
+ 				match = (page[i] == image[i]);
+ 			else
+ 				match = false;
+ 		}
+ 		else
+ 		{
+ 			match = true;
+ 		}
+ 
+ 		if (match)
+ 		{
+ 			if (fragmentBegin >= 0)
+ 			{
+ 				/* Matched byte is potential of fragment. */
+ 				if (fragmentEnd < 0)
+ 					fragmentEnd = i;
+ 
+ 				/*
+ 				 * Write next fragment if sequence of matched bytes is longer
+ 				 * than MATCH_THRESHOLD.
+ 				 */
+ 				if (i - fragmentEnd >= MATCH_THRESHOLD)
+ 				{
+ 					writeFragment(pageData, fragmentBegin,
+ 								  fragmentEnd - fragmentBegin,
+ 								  page + fragmentBegin);
+ 					fragmentBegin = -1;
+ 					fragmentEnd = -1;
+ 				}
+ 			}
+ 		}
+ 		else
+ 		{
+ 			/* On unmatched byte, start new fragment if it's not done yet */
+ 			if (fragmentBegin < 0)
+ 				fragmentBegin = i;
+ 			fragmentEnd = -1;
+ 		}
+ 	}
+ 
+ 	if (fragmentBegin >= 0)
+ 		writeFragment(pageData, fragmentBegin,
+ 					  BLCKSZ - fragmentBegin,
+ 					  page + fragmentBegin);
+ 
+ #ifdef USE_ASSERT_CHECKING
+ 	/*
+ 	 * If assert checking is enabled check produced delta.  Result of delta
+ 	 * application to saved image should be the same as current page state.
+ 	 */
+ 	do
+ 	{
+ 		char	tmp[BLCKSZ];
+ 		memcpy(tmp, image, BLCKSZ);
+ 		applyPageRedo(tmp, pageData->data, pageData->dataLen);
+ 		Assert(memcmp(tmp, page, BLCKSZ) == 0);
+ 	} while (false);
+ #endif
+ }
+ 
+ /*
+  * Start new generic xlog record.
+  */
+ void
+ GenericXLogStart(Relation relation)
+ {
+ 	int i;
+ 
+ 	if (genericXlogStatus != GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogStart: generic xlog is already started")));
+ 
+ 	genericXlogStatus = RelationNeedsWAL(relation) ? GXLOG_LOGGED : GXLOG_UNLOGGED;
+ 
+ 	for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 	{
+ 		pages[i].buffer = InvalidBuffer;
+ 	}
+ }
+ 
+ /*
+  * Register new buffer for generic xlog record.
+  */
+ Page
+ GenericXLogRegister(Buffer buffer, bool isNew)
+ {
+ 	int block_id;
+ 
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogRegister: generic xlog isn't started")));
+ 
+ 	/* Place new buffer to unused slot in array */
+ 	for (block_id = 0; block_id < MAX_GENERIC_XLOG_PAGES; block_id++)
+ 	{
+ 		if (BufferIsInvalid(pages[block_id].buffer))
+ 		{
+ 			pages[block_id].buffer = buffer;
+ 			memcpy(pages[block_id].image, BufferGetPage(buffer), BLCKSZ);
+ 			pages[block_id].dataLen = 0;
+ 			pages[block_id].fullImage = isNew;
+ 			return (Page)pages[block_id].image;
+ 		}
+ 		else if (pages[block_id].buffer == buffer)
+ 		{
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 					 errmsg("GenericXLogRegister: duplicate buffer %d", buffer)));
+ 		}
+ 	}
+ 
+ 	ereport(ERROR,
+ 			(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ 			 errmsg("GenericXLogRegister: maximum number of %d buffers is exceeded",
+ 					MAX_GENERIC_XLOG_PAGES)));
+ 
+ 	/* keep compiler quiet */
+ 	return NULL;
+ }
+ 
+ /*
+  * Unregister particular buffer for generic xlog record.
+  */
+ void
+ GenericXLogUnregister(Buffer buffer)
+ {
+ 	int block_id;
+ 
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogUnregister: generic xlog isn't started")));
+ 
+ 	/* Find block in array to unregister */
+ 	for (block_id = 0; block_id < MAX_GENERIC_XLOG_PAGES; block_id++)
+ 	{
+ 		if (pages[block_id].buffer == buffer)
+ 		{
+ 			/*
+ 			 * Preserve order of pages in array because it could matter for
+ 			 * concurrency.
+ 			 */
+ 			memmove(&pages[block_id], &pages[block_id + 1],
+ 					(MAX_GENERIC_XLOG_PAGES - block_id - 1) * sizeof(PageData));
+ 			pages[MAX_GENERIC_XLOG_PAGES - 1].buffer = InvalidBuffer;
+ 			return;
+ 		}
+ 	}
+ 
+ 	ereport(ERROR,
+ 			(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 			 errmsg("GenericXLogUnregister: registered buffer not found")));
+ }
+ 
+ /*
+  * Put all changes in registered buffers to generic xlog record.
+  */
+ XLogRecPtr
+ GenericXLogFinish(void)
+ {
+ 	XLogRecPtr lsn = InvalidXLogRecPtr;
+ 	int i;
+ 
+ 	if (genericXlogStatus == GXLOG_LOGGED)
+ 	{
+ 		/* Logged relation: make xlog record in critical section. */
+ 		START_CRIT_SECTION();
+ 		XLogBeginInsert();
+ 
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			char	tmp[BLCKSZ];
+ 
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 
+ 			/* Swap current and saved page image. */
+ 			memcpy(tmp, pages[i].image, BLCKSZ);
+ 			memcpy(pages[i].image, BufferGetPage(pages[i].buffer), BLCKSZ);
+ 			memcpy(BufferGetPage(pages[i].buffer), tmp, BLCKSZ);
+ 
+ 			if (pages[i].fullImage)
+ 			{
+ 				/* Full page image doesn't require anything special from us */
+ 				XLogRegisterBuffer(i, pages[i].buffer, REGBUF_FORCE_IMAGE);
+ 			}
+ 			else
+ 			{
+ 				/*
+ 				 * In normal node calculate delta and write use it as data
+ 				 * associated with this page.
+ 				 */
+ 				XLogRegisterBuffer(i, pages[i].buffer, REGBUF_STANDARD);
+ 				writeDelta(&pages[i]);
+ 				XLogRegisterBufData(i, pages[i].data, pages[i].dataLen);
+ 			}
+ 		}
+ 
+ 		/* Insert xlog record */
+ 		lsn = XLogInsert(RM_GENERIC_ID, 0);
+ 
+ 		/* Set LSN and make buffers dirty */
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 			PageSetLSN(BufferGetPage(pages[i].buffer), lsn);
+ 			MarkBufferDirty(pages[i].buffer);
+ 		}
+ 		END_CRIT_SECTION();
+ 	}
+ 	else if (genericXlogStatus == GXLOG_UNLOGGED)
+ 	{
+ 		/* Unlogged relation: skip xlog-related stuff */
+ 		START_CRIT_SECTION();
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 			memcpy(BufferGetPage(pages[i].buffer), pages[i].image, BLCKSZ);
+ 			MarkBufferDirty(pages[i].buffer);
+ 		}
+ 		END_CRIT_SECTION();
+ 	}
+ 	else
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogFinish: generic xlog isn't started")));
+ 	}
+ 
+ 	genericXlogStatus = GXLOG_NOT_STARTED;
+ 
+ 	return lsn;
+ }
+ 
+ /*
+  * Abort generic xlog record.
+  */
+ void
+ GenericXLogAbort(void)
+ {
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogAbort: generic xlog isn't started")));
+ 
+ 	genericXlogStatus = GXLOG_NOT_STARTED;
+ }
+ 
+ /*
+  * Apply delta to given page image.
+  */
+ static void
+ applyPageRedo(Page page, Pointer data, Size dataSize)
+ {
+ 	Pointer ptr = data, end = data + dataSize;
+ 
+ 	while (ptr < end)
+ 	{
+ 		OffsetNumber	offset,
+ 						length;
+ 
+ 		memcpy(&offset, ptr, sizeof(offset));
+ 		ptr += sizeof(offset);
+ 		memcpy(&length, ptr, sizeof(length));
+ 		ptr += sizeof(length);
+ 
+ 		memcpy(page + offset, ptr, length);
+ 
+ 		ptr += length;
+ 	}
+ }
+ 
+ /*
+  * Redo function for generic xlog record.
+  */
+ void
+ generic_redo(XLogReaderState *record)
+ {
+ 	uint8		block_id;
+ 	Buffer		buffers[MAX_GENERIC_XLOG_PAGES] = {InvalidBuffer};
+ 	XLogRecPtr	lsn = record->EndRecPtr;
+ 
+ 	Assert(record->max_block_id < MAX_GENERIC_XLOG_PAGES);
+ 
+ 	/* Interate over blocks */
+ 	for (block_id = 0; block_id <= record->max_block_id; block_id++)
+ 	{
+ 		XLogRedoAction action;
+ 
+ 		if (!XLogRecHasBlockRef(record, block_id))
+ 			continue;
+ 
+ 		action = XLogReadBufferForRedo(record, block_id, &buffers[block_id]);
+ 
+ 		/* Apply redo to given block if needed */
+ 		if (action == BLK_NEEDS_REDO)
+ 		{
+ 			Pointer	blockData;
+ 			Size	blockDataSize;
+ 			Page	page;
+ 
+ 			page = BufferGetPage(buffers[block_id]);
+ 			blockData = XLogRecGetBlockData(record, block_id, &blockDataSize);
+ 			applyPageRedo(page, blockData, blockDataSize);
+ 
+ 			PageSetLSN(page, lsn);
+ 			MarkBufferDirty(buffers[block_id]);
+ 		}
+ 	}
+ 
+ 	/* Changes are done: unlock and release all buffers */
+ 	for (block_id = 0; block_id <= record->max_block_id; block_id++)
+ 	{
+ 		if (BufferIsValid(buffers[block_id]))
+ 			UnlockReleaseBuffer(buffers[block_id]);
+ 	}
+ }
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
new file mode 100644
index 7c4d773..7b38c16
*** a/src/backend/access/transam/rmgr.c
--- b/src/backend/access/transam/rmgr.c
***************
*** 11,16 ****
--- 11,17 ----
  #include "access/commit_ts.h"
  #include "access/gin.h"
  #include "access/gist_private.h"
+ #include "access/generic_xlog.h"
  #include "access/hash.h"
  #include "access/heapam_xlog.h"
  #include "access/brin_xlog.h"
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
new file mode 100644
index 88c3a49..2d69dc2
*** a/src/backend/replication/logical/decode.c
--- b/src/backend/replication/logical/decode.c
*************** LogicalDecodingProcessRecord(LogicalDeco
*** 135,140 ****
--- 135,141 ----
  		case RM_BRIN_ID:
  		case RM_COMMIT_TS_ID:
  		case RM_REPLORIGIN_ID:
+ 		case RM_GENERIC_ID:
  			break;
  		case RM_NEXT_ID:
  			elog(ERROR, "unexpected RM_NEXT_ID rmgr_id: %u", (RmgrIds) XLogRecGetRmid(buf.record));
diff --git a/src/bin/pg_xlogdump/.gitignore b/src/bin/pg_xlogdump/.gitignore
new file mode 100644
index eebaf30..33a1acf
*** a/src/bin/pg_xlogdump/.gitignore
--- b/src/bin/pg_xlogdump/.gitignore
***************
*** 4,9 ****
--- 4,10 ----
  /clogdesc.c
  /committsdesc.c
  /dbasedesc.c
+ /genericdesc.c
  /gindesc.c
  /gistdesc.c
  /hashdesc.c
diff --git a/src/bin/pg_xlogdump/rmgrdesc.c b/src/bin/pg_xlogdump/rmgrdesc.c
new file mode 100644
index f9cd395..cff7e59
*** a/src/bin/pg_xlogdump/rmgrdesc.c
--- b/src/bin/pg_xlogdump/rmgrdesc.c
***************
*** 11,16 ****
--- 11,17 ----
  #include "access/brin_xlog.h"
  #include "access/clog.h"
  #include "access/commit_ts.h"
+ #include "access/generic_xlog.h"
  #include "access/gin.h"
  #include "access/gist_private.h"
  #include "access/hash.h"
diff --git a/src/include/access/generic_xlog.h b/src/include/access/generic_xlog.h
new file mode 100644
index ...9a10c2c
*** a/src/include/access/generic_xlog.h
--- b/src/include/access/generic_xlog.h
***************
*** 0 ****
--- 1,92 ----
+ /*-------------------------------------------------------------------------
+  *
+  * generic_xlog.h
+  *	  Generic xlog API definition.
+  *
+  *
+  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/access/generic_xlog.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef GENERIC_XLOG_H
+ #define GENERIC_XLOG_H
+ 
+ #include "access/xlog.h"
+ #include "access/xlog_internal.h"
+ #include "storage/bufpage.h"
+ #include "utils/rel.h"
+ 
+ #define MAX_GENERIC_XLOG_PAGES	  3
+ 
+ /*-------------------------------------------------------------------------
+  * API for construction of generic xlog records
+  *
+  * This API allows user to construct generic xlog records which are
+  * describing difference between pages in general way.  Thus it's useful
+  * for extension which provides custom access methods because they couldn't
+  * register their own WAL redo routines.
+  *
+  * Generic xlog record should be constructed in following steps.
+  * 1) GenericXLogStart(relation) - start construction of generic xlog
+  *	  record for given relation.
+  * 2) GenericXLogRegister(buffer, isNew) - register one or more buffers
+  *	  for generic xlog record.  This function return a copy of page image
+  *	  where modifications should be performed.  The second argument
+  *	  indicates that block is new and full image should be taken.
+  * 3) Do modification of page images obtained in previous step.
+  * 4) GenericXLogFinish() - finish construction of generic xlog record.
+  *
+  * Please, note following points while constructing generic xlog records.
+  * - No direct modifications of page images are allowed! All modifications
+  *	 should be done in copies returned by GenericXLogRegister().  Literally
+  *	 code which makes generic xlog records should never call
+  *	 BufferGetPage() function.
+  * - On any step generic xlog record construction could be canceled by
+  *	 calling GenericXLogAbort().  All changes made in page images copies
+  *	 would be discarded.
+  * - Registrations of buffers (step 2) and modifications of page images
+  *	 (step 3) could be mixed in any sequence.  The only restriction is that
+  *	 you can modify page image only after registration of corresponding
+  *	 buffer.
+  * - After registration buffer also can be unregistered by calling
+  *	 GenericXLogUnregister(buffer).  In this case changes made in particular
+  *	 page image copy will be discarded.
+  * - Generic xlog assumes that pages are using standard layout.  I.e. all
+  *	 information between pd_lower and pd_upper will be discarded.
+  * - Maximum number of buffers simultaneously registered for generic xlog
+  *	 is MAX_GENERIC_XLOG_PAGES.  Error would be thrown if this limit
+  *	 exceeded.
+  * - Since you modify copies of page images, GenericXLogStart() doesn't
+  *	 start a critical section.  Thus, you can do memory allocation, error
+  *	 throwing etc between GenericXLogStart() and GenericXLogFinish().
+  *	 Actual critical section present inside GenericXLogFinish().
+  * - GenericXLogFinish() takes care about marking buffers dirty and setting
+  *	 their LSNs.  You don't need to do this explicitly.
+  * - For unlogged relations, everything work the same expect there is no
+  *	 WAL record produced.  Thus, you typically don't need to do any explicit
+  *	 checks for unlogged relations.
+  * - If registered buffer isn't new, generic xlog record contains delta
+  *	 between old and new page images.  This delta is produced by per byte
+  *	 comparison.  Current delta mechanist is not effective for data shift
+  *	 inside the page.  However, it could be improved in further versions.
+  * - Generic xlog redo function will acquire exclusive locks to buffers
+  *	 in the same order they were registered.  After redo of all changes
+  *	 locks would be released in the same order.  That could makes sense for
+  *	 concurrency.
+  *-------------------------------------------------------------------------
+  */
+ extern void GenericXLogStart(Relation relation);
+ extern Page GenericXLogRegister(Buffer buffer, bool isNew);
+ extern void GenericXLogUnregister(Buffer buffer);
+ extern XLogRecPtr GenericXLogFinish(void);
+ extern void GenericXLogAbort(void);
+ 
+ /* functions defined for rmgr */
+ extern void generic_redo(XLogReaderState *record);
+ extern const char *generic_identify(uint8 info);
+ extern void generic_desc(StringInfo buf, XLogReaderState *record);
+ 
+ #endif   /* GENERIC_XLOG_H */
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
new file mode 100644
index fab912d..3cfe6f7
*** a/src/include/access/rmgrlist.h
--- b/src/include/access/rmgrlist.h
*************** PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo
*** 45,47 ****
--- 45,48 ----
  PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL)
  PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL)
  PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL)
+ PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL)
bloom-contrib.7.patchapplication/octet-stream; name=bloom-contrib.7.patchDownload
diff --git a/contrib/Makefile b/contrib/Makefile
new file mode 100644
index bd251f6..3ac3818
*** a/contrib/Makefile
--- b/contrib/Makefile
*************** SUBDIRS = \
*** 8,13 ****
--- 8,14 ----
  		adminpack	\
  		auth_delay	\
  		auto_explain	\
+ 		bloom		\
  		btree_gin	\
  		btree_gist	\
  		chkpass		\
diff --git a/contrib/bloom/.gitignore b/contrib/bloom/.gitignore
new file mode 100644
index ...5dcb3ff
*** a/contrib/bloom/.gitignore
--- b/contrib/bloom/.gitignore
***************
*** 0 ****
--- 1,4 ----
+ # Generated subdirectories
+ /log/
+ /results/
+ /tmp_check/
diff --git a/contrib/bloom/Makefile b/contrib/bloom/Makefile
new file mode 100644
index ...f540027
*** a/contrib/bloom/Makefile
--- b/contrib/bloom/Makefile
***************
*** 0 ****
--- 1,21 ----
+ # contrib/bloom/Makefile
+ 
+ MODULE_big = bloom
+ OBJS = blcost.o blinsert.o blscan.o blutils.o blvacuum.o blvalidate.o $(WIN32RES)
+ 
+ EXTENSION = bloom
+ DATA = bloom--1.0.sql
+ PGFILEDESC = "bloom access method - signature file based index"
+ 
+ REGRESS = bloom
+ 
+ ifdef USE_PGXS
+ PG_CONFIG = pg_config
+ PGXS := $(shell $(PG_CONFIG) --pgxs)
+ include $(PGXS)
+ else
+ subdir = contrib/bloom
+ top_builddir = ../..
+ include $(top_builddir)/src/Makefile.global
+ include $(top_srcdir)/contrib/contrib-global.mk
+ endif
diff --git a/contrib/bloom/blcost.c b/contrib/bloom/blcost.c
new file mode 100644
index ...d918dce
*** a/contrib/bloom/blcost.c
--- b/contrib/bloom/blcost.c
***************
*** 0 ****
--- 1,45 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blcost.c
+  *		Cost estimate function for bloom indexes.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blcost.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "fmgr.h"
+ #include "optimizer/cost.h"
+ #include "utils/selfuncs.h"
+ 
+ #include "bloom.h"
+ 
+ void
+ blcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
+ 			   Cost *indexStartupCost, Cost *indexTotalCost,
+ 			   Selectivity *indexSelectivity, double *indexCorrelation)
+ {
+ 	IndexOptInfo   *index = path->indexinfo;
+ 	List		   *qinfos;
+ 	GenericCosts	costs;
+ 
+ 	/* Do preliminary analysis of indexquals */
+ 	qinfos = deconstruct_indexquals(path);
+ 
+ 	MemSet(&costs, 0, sizeof(costs));
+ 
+ 	/* We have to visit all index tuples anyway */
+ 	costs.numIndexTuples = index->tuples;
+ 
+ 	/* Use generic estimate */
+ 	genericcostestimate(root, path, loop_count, qinfos, &costs);
+ 
+ 	*indexStartupCost = costs.indexStartupCost;
+ 	*indexTotalCost = costs.indexTotalCost;
+ 	*indexSelectivity = costs.indexSelectivity;
+ 	*indexCorrelation = costs.indexCorrelation;
+ }
diff --git a/contrib/bloom/blinsert.c b/contrib/bloom/blinsert.c
new file mode 100644
index ...15bff40
*** a/contrib/bloom/blinsert.c
--- b/contrib/bloom/blinsert.c
***************
*** 0 ****
--- 1,293 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blinsert.c
+  *		Bloom index build and insert functions.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blinsert.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "access/generic_xlog.h"
+ #include "catalog/index.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ 
+ #include "bloom.h"
+ 
+ PG_MODULE_MAGIC;
+ 
+ typedef struct
+ {
+ 	BloomState		blstate;
+ 	MemoryContext	tmpCtx;
+ 	char			data[BLCKSZ];
+ 	int64			count;
+ } BloomBuildState;
+ 
+ /*
+  * Flush page cached in BloomBuildState.
+  */
+ static void
+ flushCachedPage(Relation index, BloomBuildState *buildstate)
+ {
+ 	Page	page;
+ 	Buffer	buffer = BloomNewBuffer(index);
+ 
+ 	GenericXLogStart(index);
+ 	page = GenericXLogRegister(buffer, true);
+ 	memcpy(page, buildstate->data, BLCKSZ);
+ 	GenericXLogFinish();
+ 	UnlockReleaseBuffer(buffer);
+ }
+ 
+ /*
+  * (Re)initialize cached page in BloomBuildState.
+  */
+ static void
+ initCachedPage(BloomBuildState *buildstate)
+ {
+ 	memset(buildstate->data, 0, BLCKSZ);
+ 	BloomInitPage(buildstate->data, 0);
+ 	buildstate->count = 0;
+ }
+ 
+ /*
+  * Per-tuple callback from IndexBuildHeapScan.
+  */
+ static void
+ bloomBuildCallback(Relation index, HeapTuple htup, Datum *values,
+ 					bool *isnull, bool tupleIsAlive, void *state)
+ {
+ 	BloomBuildState	*buildstate = (BloomBuildState *)state;
+ 	MemoryContext	 oldCtx;
+ 	BloomTuple		*itup;
+ 
+ 	oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
+ 
+ 	itup = BloomFormTuple(&buildstate->blstate, &htup->t_self, values, isnull);
+ 
+ 	/* Try to add next item to cached page */
+ 	if (BloomPageAddItem(&buildstate->blstate, buildstate->data, itup) == false)
+ 	{
+ 		/* Cached page is full, flush it out and make a new one */
+ 		flushCachedPage(index, buildstate);
+ 
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		initCachedPage(buildstate);
+ 
+ 		if (BloomPageAddItem(&buildstate->blstate, buildstate->data, itup) == false)
+ 		{
+ 			/* We shouldn't be here since we're inserting to the empty page */
+ 			elog(ERROR, "can not add new tuple");
+ 		}
+ 	}
+ 	else
+ 	{
+ 		buildstate->count++;
+ 	}
+ 
+ 	MemoryContextSwitchTo(oldCtx);
+ 	MemoryContextReset(buildstate->tmpCtx);
+ }
+ 
+ IndexBuildResult *
+ blbuild(Relation heap, Relation index, IndexInfo *indexInfo)
+ {
+ 	IndexBuildResult   *result;
+ 	double				reltuples;
+ 	BloomBuildState		buildstate;
+ 
+ 	if (RelationGetNumberOfBlocks(index) != 0)
+ 		elog(ERROR, "index \"%s\" already contains data",
+ 			RelationGetRelationName(index));
+ 
+ 	/* Initialize the meta page */
+ 	BloomInitMetapage(index);
+ 
+ 	initBloomState(&buildstate.blstate, index);
+ 
+ 	buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
+ 												"Bloom build temporary context",
+ 												ALLOCSET_DEFAULT_MINSIZE,
+ 												ALLOCSET_DEFAULT_INITSIZE,
+ 												ALLOCSET_DEFAULT_MAXSIZE);
+ 
+ 	initCachedPage(&buildstate);
+ 
+ 	/* Do the heap scan */
+ 	reltuples = IndexBuildHeapScan(heap, index, indexInfo, true,
+ 									bloomBuildCallback, (void *) &buildstate);
+ 
+ 	if (buildstate.count > 0)
+ 		flushCachedPage(index, &buildstate);
+ 
+ 	MemoryContextDelete(buildstate.tmpCtx);
+ 
+ 	result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
+ 	result->heap_tuples = result->index_tuples = reltuples;
+ 
+ 	return result;
+ }
+ 
+ void
+ blbuildempty(Relation index)
+ {
+ 	if (RelationGetNumberOfBlocks(index) != 0)
+ 		elog(ERROR, "index \"%s\" already contains data",
+ 			RelationGetRelationName(index));
+ 
+ 	/* Initialize the meta page */
+ 	BloomInitMetapage(index);
+ }
+ 
+ bool
+ blinsert(Relation index, Datum *values, bool *isnull,
+ 		ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique)
+ {
+ 	BloomState			blstate;
+ 	BloomTuple		   *itup;
+ 	MemoryContext		oldCtx;
+ 	MemoryContext		insertCtx;
+ 	BloomMetaPageData  *metaData;
+ 	Buffer				buffer,
+ 						metaBuffer;
+ 	Page				page,
+ 						metaPage;
+ 	BlockNumber			blkno = InvalidBlockNumber;
+ 	OffsetNumber		nStart;
+ 
+ 	insertCtx = AllocSetContextCreate(CurrentMemoryContext,
+ 										"Bloom insert temporary context",
+ 										ALLOCSET_DEFAULT_MINSIZE,
+ 										ALLOCSET_DEFAULT_INITSIZE,
+ 										ALLOCSET_DEFAULT_MAXSIZE);
+ 
+ 	oldCtx = MemoryContextSwitchTo(insertCtx);
+ 
+ 	initBloomState(&blstate, index);
+ 	itup = BloomFormTuple(&blstate, ht_ctid, values, isnull);
+ 
+ 	/*
+ 	 * At first, try to insert new tuple to the first page in
+ 	 * notFullPage array.  If success we don't need to modify the
+ 	 * meta page.
+ 	 */
+ 	metaBuffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 	LockBuffer(metaBuffer, BUFFER_LOCK_SHARE);
+ 	metaData = BloomPageGetMeta(BufferGetPage(metaBuffer));
+ 
+ 	if (metaData->nEnd > metaData->nStart)
+ 	{
+ 		Page	page;
+ 
+ 		blkno = metaData->notFullPage[metaData->nStart];
+ 
+ 		Assert(blkno != InvalidBlockNumber);
+ 		LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		GenericXLogStart(index);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		if (BloomPageAddItem(&blstate, page, itup))
+ 		{
+ 			GenericXLogFinish();
+ 			UnlockReleaseBuffer(buffer);
+ 			ReleaseBuffer(metaBuffer);
+ 			goto away;
+ 		}
+ 		else
+ 		{
+ 			GenericXLogAbort();
+ 			UnlockReleaseBuffer(buffer);
+ 		}
+ 	}
+ 	else
+ 	{
+ 		/* First page in notFullPage isn't suitable */
+ 		LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
+ 	}
+ 
+ 	/*
+ 	 * Try other pages in notFullPage array.  We will have to change nStart
+ 	 * in metapage.  Thus, grab exclusive lock on metapage.
+ 	 */
+ 	LockBuffer(metaBuffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 	GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(metaBuffer, false);
+ 	metaData = BloomPageGetMeta(metaPage);
+ 
+ 	/*
+ 	 * Iterate over notFullPage array.  Skip page we already tried first.
+ 	 */
+ 	nStart = metaData->nStart;
+ 	if (metaData->nEnd > nStart &&
+ 		blkno == metaData->notFullPage[nStart] )
+ 		nStart++;
+ 
+ 	while (metaData->nEnd > nStart)
+ 	{
+ 		blkno = metaData->notFullPage[nStart];
+ 		Assert(blkno != InvalidBlockNumber);
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		if (BloomPageAddItem(&blstate, page, itup))
+ 		{
+ 			metaData->nStart = nStart;
+ 			GenericXLogFinish();
+ 			UnlockReleaseBuffer(buffer);
+ 			UnlockReleaseBuffer(metaBuffer);
+ 			goto away;
+ 		}
+ 		else
+ 		{
+ 			GenericXLogUnregister(buffer);
+ 			UnlockReleaseBuffer(buffer);
+ 		}
+ 		nStart++;
+ 	}
+ 
+ 	GenericXLogAbort();
+ 
+ 	/*
+ 	 * Didn't find place to insert in notFullPage array.  Allocate new page.
+ 	 */
+ 	buffer = BloomNewBuffer(index);
+ 
+ 	GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(metaBuffer, false);
+ 	metaData = BloomPageGetMeta(metaPage);
+ 	page = GenericXLogRegister(buffer, true);
+ 	BloomInitPage(page, 0);
+ 	BloomPageAddItem(&blstate, page, itup);
+ 
+ 	metaData->nStart = 0;
+ 	metaData->nEnd = 1;
+ 	metaData->notFullPage[ 0 ] = BufferGetBlockNumber(buffer);
+ 
+ 	GenericXLogFinish();
+ 
+ 	UnlockReleaseBuffer(buffer);
+ 	UnlockReleaseBuffer(metaBuffer);
+ 
+ away:
+ 	MemoryContextSwitchTo(oldCtx);
+ 	MemoryContextDelete(insertCtx);
+ 
+ 	return false;
+ }
diff --git a/contrib/bloom/bloom--1.0.sql b/contrib/bloom/bloom--1.0.sql
new file mode 100644
index ...7fa7513
*** a/contrib/bloom/bloom--1.0.sql
--- b/contrib/bloom/bloom--1.0.sql
***************
*** 0 ****
--- 1,19 ----
+ CREATE OR REPLACE FUNCTION blhandler(internal)
+ RETURNS index_am_handler
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C;
+ 
+ -- Access method
+ CREATE ACCESS METHOD bloom TYPE INDEX HANDLER blhandler;
+ 
+ -- Opclasses
+ 
+ CREATE OPERATOR CLASS int4_ops
+ DEFAULT FOR TYPE int4 USING bloom AS
+ 	OPERATOR	1	=(int4, int4),
+ 	FUNCTION	1	hashint4(int4);
+ 
+ CREATE OPERATOR CLASS text_ops
+ DEFAULT FOR TYPE text USING bloom AS
+ 	OPERATOR	1	=(text, text),
+ 	FUNCTION	1	hashtext(text);
diff --git a/contrib/bloom/bloom.control b/contrib/bloom/bloom.control
new file mode 100644
index ...4d4124b
*** a/contrib/bloom/bloom.control
--- b/contrib/bloom/bloom.control
***************
*** 0 ****
--- 1,5 ----
+ # bloom extension
+ comment = 'bloom access method - signature file based index'
+ default_version = '1.0'
+ module_pathname = '$libdir/bloom'
+ relocatable = true
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
new file mode 100644
index ...6f20ee4
*** a/contrib/bloom/bloom.h
--- b/contrib/bloom/bloom.h
***************
*** 0 ****
--- 1,176 ----
+ /*-------------------------------------------------------------------------
+  *
+  * bloom.h
+  *	  Header for bloom index.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/bloom.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef _BLOOM_H_
+ #define _BLOOM_H_
+ 
+ #include "access/amapi.h"
+ #include "access/generic_xlog.h"
+ #include "access/itup.h"
+ #include "access/xlog.h"
+ #include "nodes/relation.h"
+ #include "fmgr.h"
+ 
+ /* Support procedures numbers */
+ #define	BLOOM_HASH_PROC			1
+ #define	BLOOM_NPROC				1
+ 
+ /* Scan strategies */
+ #define	BLOOM_EQUAL_STRATEGY	1
+ #define	BLOOM_NSTRATEGIES		1
+ 
+ /* Opaque for bloom pages */
+ typedef struct BloomPageOpaqueData
+ {
+ 	OffsetNumber	maxoff;
+ 	uint16			flags;
+ } BloomPageOpaqueData;
+ 
+ typedef BloomPageOpaqueData *BloomPageOpaque;
+ 
+ /* Bloom page flags */
+ #define BLOOM_META		(1<<0)
+ #define BLOOM_DELETED	(2<<0)
+ 
+ #define BloomPageGetOpaque(page) ((BloomPageOpaque) PageGetSpecialPointer(page))
+ #define BloomPageGetMaxOffset(page) (BloomPageGetOpaque(page)->maxoff)
+ #define BloomPageIsMeta(page) (BloomPageGetOpaque(page)->flags & BLOOM_META)
+ #define BloomPageIsDeleted(page) (BloomPageGetOpaque(page)->flags & BLOOM_DELETED)
+ #define BloomPageSetDeleted(page) (BloomPageGetOpaque(page)->flags |= BLOOM_DELETED)
+ #define BloomPageSetNonDeleted(page) (BloomPageGetOpaque(page)->flags &= ~BLOOM_DELETED)
+ #define BloomPageGetData(page)		((BloomTuple *)PageGetContents(page))
+ #define BloomPageGetTuple(state, page, offset) \
+ 	((BloomTuple *)(PageGetContents(page) \
+ 		+ (state)->sizeOfBloomTuple * ((offset) - 1)))
+ #define BloomPageGetNextTuple(state, tuple) \
+ 	((BloomTuple *)((Pointer)(tuple) + (state)->sizeOfBloomTuple))
+ 
+ /* Preserved page numbers */
+ #define BLOOM_METAPAGE_BLKNO	(0)
+ #define BLOOM_HEAD_BLKNO		(1) /* first data page */
+ 
+ /* Bloom index options */
+ typedef struct BloomOptions
+ {
+ 	int32	vl_len_;				 /* varlena header (do not touch directly!) */
+ 	int		bloomLength;			 /* length of signature in uint16 */
+ 	int		bitSize[INDEX_MAX_KEYS]; /* signature bits per index key */
+ } BloomOptions;
+ 
+ /*
+  * FreeBlockNumberArray - array of block numbers sized so that metadata fill
+  * all space in metapage.
+  */
+ typedef BlockNumber FreeBlockNumberArray[
+ 	MAXALIGN_DOWN(
+ 		BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(BloomPageOpaqueData))
+ 			   - MAXALIGN(sizeof(uint16) * 2 + sizeof(uint32) + sizeof(BloomOptions))
+ 	) / sizeof(BlockNumber) 
+ ];
+ 
+ /* Metadata of bloom index */
+ typedef struct BloomMetaPageData
+ {
+ 	uint32					magickNumber;
+ 	uint16					nStart;
+ 	uint16					nEnd;
+ 	BloomOptions			opts;
+ 	FreeBlockNumberArray	notFullPage;
+ } BloomMetaPageData;
+ 
+ /* Magic number to distinguish bloom pages among anothers */
+ #define BLOOM_MAGICK_NUMBER	(0xDBAC0DED)
+ 
+ /* Number of blocks numbers fit in BloomMetaPageData */
+ #define BloomMetaBlockN		(sizeof(FreeBlockNumberArray) / sizeof(BlockNumber))
+ 
+ #define BloomPageGetMeta(page)	((BloomMetaPageData *) PageGetContents(page))
+ 
+ typedef struct BloomState 
+ {
+ 	FmgrInfo			hashFn[INDEX_MAX_KEYS];
+ 	BloomOptions	   *opts; /* stored in rd_amcache and defined at creation time */
+ 	int32				nColumns;
+ 	/* 
+ 	 * sizeOfBloomTuple is index's specific, and it depends on
+ 	 * reloptions, so precompute it
+ 	 */
+ 	int32				sizeOfBloomTuple; 
+ } BloomState;
+ 
+ #define BloomPageGetFreeSpace(state, page) \
+ 	(BLCKSZ - MAXALIGN(SizeOfPageHeaderData) \
+ 		- BloomPageGetMaxOffset(page) * (state)->sizeOfBloomTuple \
+ 		- MAXALIGN(sizeof(BloomPageOpaqueData)))
+ 
+ /*
+  * Tuples are very different from all other relations
+  */
+ typedef uint16	SignType;
+ 
+ typedef struct BloomTuple
+ {
+ 	ItemPointerData		heapPtr;
+ 	SignType			sign[1];
+ } BloomTuple;
+ 
+ #define BLOOMTUPLEHDRSZ	offsetof(BloomTuple, sign)
+ 
+ /* Opaque data structure for bloom index scan */
+ typedef struct BloomScanOpaqueData
+ {
+ 	SignType   *sign;	/* Scan signature */
+ 	BloomState	state;
+ } BloomScanOpaqueData;
+ 
+ typedef BloomScanOpaqueData *BloomScanOpaque;
+ 
+ /* blutils.c */
+ extern void _PG_init(void);
+ extern Datum blhandler(PG_FUNCTION_ARGS);
+ extern void initBloomState(BloomState *state, Relation index);
+ extern void BloomInitMetapage(Relation index);
+ extern void BloomInitPage(Page page, uint16 flags);
+ extern Buffer BloomNewBuffer(Relation index);
+ extern void signValue(BloomState *state, SignType *sign, Datum value, int attno);
+ extern BloomTuple* BloomFormTuple(BloomState *state, ItemPointer iptr, Datum *values, bool *isnull);
+ extern bool BloomPageAddItem(BloomState *state, Page page, BloomTuple *tuple);
+ 
+ /* blvalidate.c */
+ extern bool blvalidate(Oid opclassoid);
+ 
+ /* interface functions */
+ extern bool blinsert(Relation index, Datum *values, bool *isnull,
+ 					 ItemPointer ht_ctid, Relation heapRel,
+ 					 IndexUniqueCheck checkUnique);
+ extern IndexScanDesc blbeginscan(Relation r, int nkeys, int norderbys);
+ extern int64 blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
+ extern void blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ 					 ScanKey orderbys, int norderbys);
+ extern void blendscan(IndexScanDesc scan);
+ extern void blmarkpos(IndexScanDesc scan);
+ extern void blrestrpos(IndexScanDesc scan);
+ extern IndexBuildResult *blbuild(Relation heap, Relation index,
+ 								   struct IndexInfo *indexInfo);
+ extern void blbuildempty(Relation index);
+ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
+ 	IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback,
+ 	void *callback_state);
+ extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
+ 	IndexBulkDeleteResult *stats);
+ extern bytea *bloptions(Datum reloptions, bool validate);
+ extern void blcostestimate(PlannerInfo *root, IndexPath *path,
+ 						   double loop_count, Cost *indexStartupCost,
+ 						   Cost *indexTotalCost, Selectivity *indexSelectivity,
+ 						   double *indexCorrelation);
+ 
+ #endif
diff --git a/contrib/bloom/blscan.c b/contrib/bloom/blscan.c
new file mode 100644
index ...467a1b1
*** a/contrib/bloom/blscan.c
--- b/contrib/bloom/blscan.c
***************
*** 0 ****
--- 1,174 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blscan.c
+  *		Bloom index scan functions.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blscan.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/relscan.h"
+ #include "pgstat.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/lmgr.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ 
+ #include "bloom.h"
+ 
+ IndexScanDesc
+ blbeginscan(Relation r, int nkeys, int norderbys)
+ {
+ 	IndexScanDesc scan;
+ 
+ 	scan = RelationGetIndexScan(r, nkeys, norderbys);
+ 
+ 	return scan;
+ }
+ 
+ void
+ blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ 							 ScanKey orderbys, int norderbys)
+ {
+ 	BloomScanOpaque so;
+ 
+ 	so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so == NULL)
+ 	{
+ 		/* if called from blbeginscan */
+ 		so = (BloomScanOpaque) palloc(sizeof(BloomScanOpaqueData));
+ 		initBloomState(&so->state, scan->indexRelation);
+ 		scan->opaque = so;
+ 
+ 	}
+ 	else
+ 	{
+ 		if (so->sign)
+ 			pfree(so->sign);
+ 	}
+ 	so->sign = NULL;
+ 
+ 	if (scankey && scan->numberOfKeys > 0)
+ 	{
+ 		memmove(scan->keyData, scankey,
+ 				scan->numberOfKeys * sizeof(ScanKeyData));
+ 	}
+ }
+ 
+ void
+ blendscan(IndexScanDesc scan)
+ {
+ 	BloomScanOpaque so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so->sign)
+ 		pfree(so->sign);
+ 	so->sign = NULL;
+ }
+ 
+ void
+ blmarkpos(IndexScanDesc scan)
+ {
+ 	elog(ERROR, "Bloom does not support mark/restore");
+ }
+ 
+ void
+ blrestrpos(IndexScanDesc scan)
+ {
+ 	elog(ERROR, "Bloom does not support mark/restore");
+ }
+ 
+ int64
+ blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
+ {
+ 	int64					ntids = 0;
+ 	BlockNumber				blkno = BLOOM_HEAD_BLKNO,
+ 							npages;
+ 	int						i;
+ 	BufferAccessStrategy	bas;
+ 	BloomScanOpaque 		so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so->sign == NULL && scan->numberOfKeys > 0)
+ 	{
+ 		/* New search: have to calculate search signature */
+ 		ScanKey skey = scan->keyData;
+ 
+ 		so->sign = palloc0(sizeof(SignType) * so->state.opts->bloomLength); 
+ 		
+ 		for(i = 0; i < scan->numberOfKeys; i++)
+ 		{
+ 			/*
+ 			 * Assume bloom-indexable operators to be strict, so nothing
+ 			 * could be found for NULL key.
+ 			 */
+ 			if (skey->sk_flags & SK_ISNULL)
+ 			{
+ 				pfree(so->sign);
+ 				so->sign = NULL;
+ 				return 0;
+ 			}
+ 
+ 			/* Add next value to the signature */
+ 			signValue(&so->state, so->sign, skey->sk_argument,
+ 					  skey->sk_attno - 1);
+ 
+ 			skey++;
+ 		}
+ 	}
+ 
+ 	/*
+ 	 * We're going to read the whole index. This is why we use appropriate
+ 	 * buffer access strategy.
+ 	 */
+ 	bas = GetAccessStrategy(BAS_BULKREAD);
+ 	npages = RelationGetNumberOfBlocks(scan->indexRelation);
+ 
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		Buffer 			buffer;
+ 		Page			page;
+ 
+ 		buffer = ReadBufferExtended(scan->indexRelation, MAIN_FORKNUM,
+ 									blkno, RBM_NORMAL, bas);
+ 
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 		page = BufferGetPage(buffer);
+ 
+ 		if (!BloomPageIsDeleted(page))
+ 		{
+ 			OffsetNumber offset, maxOffset = BloomPageGetMaxOffset(page);
+ 
+ 			for (offset = 1; offset <= maxOffset; offset++)
+ 			{
+ 				BloomTuple *itup = BloomPageGetTuple(&so->state, page, offset);
+ 				bool		res = true;
+ 
+ 				/* Check index signature with scan signature */
+ 				for (i = 0; res && i < so->state.opts->bloomLength; i++)
+ 				{
+ 					if ((itup->sign[i] & so->sign[i]) != so->sign[i])
+ 						res = false;
+ 				}
+ 
+ 				/* Add matching tuples to bitmap */
+ 				if (res)
+ 				{
+ 					tbm_add_tuples(tbm, &itup->heapPtr, 1, true);
+ 					ntids++;
+ 				}
+ 			}
+ 		}
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 		CHECK_FOR_INTERRUPTS();
+ 	}
+ 	FreeAccessStrategy(bas);
+ 
+ 	return ntids;
+ }
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
new file mode 100644
index ...d88e973
*** a/contrib/bloom/blutils.c
--- b/contrib/bloom/blutils.c
***************
*** 0 ****
--- 1,392 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blutils.c
+  *		Bloom index utilities.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blutils.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/amapi.h"
+ #include "access/generic_xlog.h"
+ #include "catalog/index.h"
+ #include "storage/lmgr.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "utils/memutils.h"
+ #include "access/reloptions.h"
+ #include "storage/freespace.h"
+ #include "storage/indexfsm.h"
+ 
+ #include "bloom.h"
+ 
+ /* Signature dealing macros */
+ #define BITSIGNTYPE	(BITS_PER_BYTE * sizeof(SignType))
+ #define GETWORD(x,i) ( *( (SignType*)(x) + (int)( (i) / BITSIGNTYPE ) ) )
+ #define CLRBIT(x,i)   GETWORD(x,i) &= ~( 0x01 << ( (i) % BITSIGNTYPE ) )
+ #define SETBIT(x,i)   GETWORD(x,i) |=  ( 0x01 << ( (i) % BITSIGNTYPE ) )
+ #define GETBIT(x,i) ( (GETWORD(x,i) >> ( (i) % BITSIGNTYPE )) & 0x01 )
+ 
+ PG_FUNCTION_INFO_V1(blhandler);
+ 
+ static relopt_kind bl_relopt_kind;
+ 
+ /*
+  * Module initialize function: initilized relation options.
+  */
+ void 
+ _PG_init(void)
+ {
+ 	int		i;
+ 	char	buf[16];
+ 
+ 	bl_relopt_kind = add_reloption_kind();
+ 
+ 	add_int_reloption(bl_relopt_kind, "length",
+ 					  "Length of signature in uint16 type", 5, 1, 256);
+ 
+ 	for(i = 0; i < INDEX_MAX_KEYS;i ++)
+ 	{
+ 		snprintf(buf, 16, "col%d", i+1);
+ 		add_int_reloption(bl_relopt_kind, buf,
+ 						  "Number of bits for corresponding column", 2, 1, 2048);
+ 	}
+ }
+ 
+ /*
+  * Bloom handler function: return IndexAmRoutine with access method parameters
+  * and callbacks.
+  */
+ Datum
+ blhandler(PG_FUNCTION_ARGS)
+ {
+ 	IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
+ 
+ 	amroutine->amstrategies = 1;
+ 	amroutine->amsupport = 1;
+ 	amroutine->amcanorder = false;
+ 	amroutine->amcanorderbyop = false;
+ 	amroutine->amcanbackward = false;
+ 	amroutine->amcanunique = false;
+ 	amroutine->amcanmulticol = true;
+ 	amroutine->amoptionalkey = true;
+ 	amroutine->amsearcharray = false;
+ 	amroutine->amsearchnulls = false;
+ 	amroutine->amstorage = false;
+ 	amroutine->amclusterable = false;
+ 	amroutine->ampredlocks = false;
+ 	amroutine->amkeytype = 0;
+ 
+ 	amroutine->aminsert = blinsert;
+ 	amroutine->ambeginscan = blbeginscan;
+ 	amroutine->amgettuple = NULL;
+ 	amroutine->amgetbitmap = blgetbitmap;
+ 	amroutine->amrescan = blrescan;
+ 	amroutine->amendscan = blendscan;
+ 	amroutine->ammarkpos = blmarkpos;
+ 	amroutine->amrestrpos = blrestrpos;
+ 	amroutine->ambuild = blbuild;
+ 	amroutine->ambuildempty = blbuildempty;
+ 	amroutine->ambulkdelete = blbulkdelete;
+ 	amroutine->amvacuumcleanup = blvacuumcleanup;
+ 	amroutine->amcanreturn = NULL;
+ 	amroutine->amcostestimate = blcostestimate;
+ 	amroutine->amoptions = bloptions;
+ 	amroutine->amvalidate = blvalidate;
+ 
+ 	PG_RETURN_POINTER(amroutine);
+ }
+ 
+ void 
+ initBloomState(BloomState *state, Relation index)
+ {
+ 	int	i;
+ 
+ 	state->nColumns = index->rd_att->natts;
+ 
+ 	/* Initialize hash function for each attribute */
+ 	for (i = 0; i < index->rd_att->natts; i++)
+ 	{
+ 		fmgr_info_copy(&(state->hashFn[i]),
+ 						index_getprocinfo(index, i + 1, BLOOM_HASH_PROC),
+ 						CurrentMemoryContext);
+ 	}
+ 
+ 	/* Inititalize amcache if needed */
+ 	if (!index->rd_amcache)
+ 	{
+ 		Buffer				buffer;
+ 		Page				page;
+ 		BloomMetaPageData	*meta;
+ 		BloomOptions		*opts;
+ 		BloomPageOpaque		opaque;
+ 
+ 		opts = MemoryContextAlloc(index->rd_indexcxt, sizeof(BloomOptions));
+ 
+ 		buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 
+ 		page = BufferGetPage(buffer);
+ 		opaque = BloomPageGetOpaque(page);
+ 
+ 		if (!BloomPageIsMeta(page))
+ 			elog(ERROR, "Relation is not a bloom index");
+ 		meta = BloomPageGetMeta(BufferGetPage(buffer));
+ 
+ 		if (meta->magickNumber != BLOOM_MAGICK_NUMBER)
+ 			elog(ERROR, "Relation is not a bloom index");
+ 
+ 		*opts = meta->opts;
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 
+ 		index->rd_amcache = (void *)opts;
+ 	}
+ 
+ 	state->opts = (BloomOptions *)index->rd_amcache;
+ 	state->sizeOfBloomTuple = BLOOMTUPLEHDRSZ +
+ 									sizeof(SignType) * state->opts->bloomLength; 
+ }
+ 
+ /*
+  * Add bits of given value to the signature.
+  */
+ void
+ signValue(BloomState *state, SignType *sign, Datum value, int attno)
+ {
+ 	uint32		hashVal;
+ 	int 		nBit, j;
+ 
+ 	/*
+ 	 * init generator with "column's" number to get
+ 	 * "hashed" seed for new value. We don't want to map
+ 	 * the same numbers from different columns into the same bits!
+ 	 */
+ 	srand(attno);
+ 
+ 	/*
+ 	 * Init hash sequence to map our value into bits. the same values
+ 	 * in different columns will be mapped into different bits because
+ 	 * of step above
+ 	 */
+ 	hashVal = DatumGetInt32(FunctionCall1(&state->hashFn[attno], value));
+ 	srand(hashVal ^ rand());
+ 
+ 	for (j = 0; j < state->opts->bitSize[attno]; j++)
+ 	{
+ 		/* prevent mutiple evaluation */
+ 		nBit = rand() % (state->opts->bloomLength * BITSIGNTYPE); 
+ 		SETBIT(sign, nBit);
+ 	}
+ }
+ 
+ /*
+  * Make bloom tuple from values.
+  */
+ BloomTuple *
+ BloomFormTuple(BloomState *state, ItemPointer iptr, Datum *values, bool *isnull)
+ {
+ 	int 		i;
+ 	BloomTuple *res = (BloomTuple *)palloc0(state->sizeOfBloomTuple);
+ 
+ 	res->heapPtr = *iptr;
+ 
+     /*
+ 	 * Blooming
+ 	 */
+ 	for (i = 0; i < state->nColumns; i++)
+ 	{
+ 		/* skip nulls */
+ 		if (isnull[i])
+ 			continue;
+ 
+ 		signValue(state, res->sign, values[i], i);
+ 	}
+ 
+ 	return res;
+ }
+ 
+ /*
+  * Add new bloom tuple to the page.
+  */
+ bool
+ BloomPageAddItem(BloomState *state, Page page, BloomTuple *tuple)
+ {
+ 	BloomTuple		   *itup;
+ 	BloomPageOpaque		opaque;
+ 	Pointer				ptr;
+ 
+ 	if (BloomPageGetFreeSpace(state, page) < state->sizeOfBloomTuple)
+ 		return false;
+ 
+ 	/* Copy new tuple to the end of page */
+ 	opaque = BloomPageGetOpaque(page);
+ 	itup = BloomPageGetTuple(state, page, opaque->maxoff + 1);
+ 	memcpy((Pointer)itup, (Pointer)tuple, state->sizeOfBloomTuple);
+ 
+ 	/* Adjust maxoff and pd_lower */
+ 	opaque->maxoff++;
+ 	ptr = (Pointer)BloomPageGetTuple(state, page, opaque->maxoff + 1);
+ 	((PageHeader) page)->pd_lower = ptr - page;
+ 
+ 	return true;
+ }
+ 
+ /*
+  * Allocate a new page (either by recycling, or by extending the index file)
+  * The returned buffer is already pinned and exclusive-locked
+  * Caller is responsible for initializing the page by calling BloomInitBuffer
+  */
+ Buffer
+ BloomNewBuffer(Relation index)
+ {
+ 	Buffer      buffer;
+ 	bool        needLock;
+ 
+ 	/* First, try to get a page from FSM */
+ 	for (;;)
+ 	{
+ 		BlockNumber blkno = GetFreeIndexPage(index);
+ 
+ 		if (blkno == InvalidBlockNumber)
+ 			break;
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 
+ 		/*
+ 		 * We have to guard against the possibility that someone else already
+ 		 * recycled this page; the buffer may be locked if so.
+ 		 */
+ 		if (ConditionalLockBuffer(buffer))
+ 		{
+ 			Page	page = BufferGetPage(buffer);
+ 
+ 			if (PageIsNew(page))
+ 				return buffer;  /* OK to use, if never initialized */
+ 
+ 			if (BloomPageIsDeleted(page))
+ 				return buffer;  /* OK to use */
+ 
+ 			LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ 		}
+ 
+ 		/* Can't use it, so release buffer and try again */
+ 		ReleaseBuffer(buffer);
+ 	}
+ 
+ 	/* Must extend the file */
+ 	needLock = !RELATION_IS_LOCAL(index);
+ 	if (needLock)
+ 		LockRelationForExtension(index, ExclusiveLock);
+ 
+ 	buffer = ReadBuffer(index, P_NEW);
+ 	LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 	if (needLock)
+ 		UnlockRelationForExtension(index, ExclusiveLock);
+ 
+ 	return buffer;
+ }
+ 
+ /*
+  * Initialize bloom page.
+  */
+ void
+ BloomInitPage(Page page, uint16 flags)
+ {
+ 	BloomPageOpaque opaque;
+ 
+ 	PageInit(page, BLCKSZ, sizeof(BloomPageOpaqueData));
+ 
+ 	opaque = BloomPageGetOpaque(page);
+ 	memset(opaque, 0, sizeof(BloomPageOpaqueData));
+ 	opaque->maxoff = 0;
+ 	opaque->flags = flags;
+ }
+ 
+ static BloomOptions *
+ makeDefaultBloomOptions(BloomOptions *opts)
+ {
+ 	int i;
+ 
+ 	if (!opts)
+ 		opts = palloc0(sizeof(BloomOptions));
+ 
+ 	if (opts->bloomLength <= 0)
+ 		opts->bloomLength = 5;
+ 
+ 	for(i = 0; i < INDEX_MAX_KEYS; i++)
+ 		if (opts->bitSize[i] <= 0
+ 				|| opts->bitSize[i] >= opts->bloomLength * sizeof(SignType))
+ 			opts->bitSize[i] = 2;
+ 
+ 	return opts;
+ }
+ 
+ /*
+  * Initialize metapage for bloom index.
+  */
+ void
+ BloomInitMetapage(Relation index)
+ {
+ 	Page				metaPage;
+ 	Buffer				metaBuffer;
+ 	BloomMetaPageData  *metadata;
+ 
+ 	metaBuffer = BloomNewBuffer(index);
+ 	Assert(BufferGetBlockNumber(metaBuffer) == BLOOM_METAPAGE_BLKNO);
+ 
+ 	GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(metaBuffer, true);
+ 
+ 	BloomInitPage(metaPage, BLOOM_META);
+ 	metadata = BloomPageGetMeta(metaPage);
+ 	memset(metadata, 0, sizeof(BloomMetaPageData));
+ 	metadata->magickNumber = BLOOM_MAGICK_NUMBER;
+ 	metadata->opts = *makeDefaultBloomOptions((BloomOptions*)index->rd_options);
+ 	((PageHeader) metaPage)->pd_lower += sizeof(BloomMetaPageData);
+ 
+ 	GenericXLogFinish();
+ 	UnlockReleaseBuffer(metaBuffer);
+ }
+ 
+ /*
+  * Initialize options for bloom index.
+  */
+ bytea *
+ bloptions(Datum reloptions, bool validate)
+ {
+ 	relopt_value	   *options;
+ 	int					numoptions;
+ 	BloomOptions	   *rdopts;
+ 	relopt_parse_elt	tab[INDEX_MAX_KEYS + 1];
+ 	int					i;
+ 	char				buf[16];
+ 
+ 	tab[0].optname = "length";
+ 	tab[0].opttype = RELOPT_TYPE_INT;
+ 	tab[0].offset = offsetof(BloomOptions, bloomLength);
+ 
+ 	for(i = 0; i < INDEX_MAX_KEYS; i++)
+ 	{
+ 		snprintf(buf, sizeof(buf), "col%d", i + 1);
+ 		tab[i + 1].optname = pstrdup(buf);
+ 		tab[i + 1].opttype = RELOPT_TYPE_INT;
+ 		tab[i + 1].offset = offsetof(BloomOptions, bitSize[i]);
+ 	}
+ 
+ 	options = parseRelOptions(reloptions, validate, bl_relopt_kind, &numoptions);
+ 	rdopts = allocateReloptStruct(sizeof(BloomOptions), options, numoptions);
+ 	fillRelOptions((void *) rdopts, sizeof(BloomOptions), options, numoptions,
+ 						validate, tab, INDEX_MAX_KEYS + 1);
+ 		
+ 	rdopts = makeDefaultBloomOptions(rdopts);
+ 
+ 	return (bytea *)rdopts;
+ }
diff --git a/contrib/bloom/blvacuum.c b/contrib/bloom/blvacuum.c
new file mode 100644
index ...c694714
*** a/contrib/bloom/blvacuum.c
--- b/contrib/bloom/blvacuum.c
***************
*** 0 ****
--- 1,195 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blvacuum.c
+  *		Bloom VACUUM functions.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blvacuum.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "catalog/storage.h"
+ #include "commands/vacuum.h"
+ #include "miscadmin.h"
+ #include "postmaster/autovacuum.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "storage/lmgr.h"
+ 
+ #include "bloom.h"
+ 
+ IndexBulkDeleteResult *
+ blbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
+ 			 IndexBulkDeleteCallback callback, void *callback_state)
+ {
+ 	Relation				index = info->index;
+ 	BlockNumber				blkno,
+ 							npages;
+ 	FreeBlockNumberArray	notFullPage;
+ 	int						countPage = 0;
+ 	BloomState				state;
+ 	Buffer					buffer;
+ 	Page					page;
+ 
+ 	if (stats == NULL)
+ 		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+ 
+ 	initBloomState(&state, index); 
+ 
+ 	/*
+ 	 * Interate over the pages. We don't care about concurrently added pages,
+ 	 * they can't contain tuples to delete.
+ 	 */
+ 	npages = RelationGetNumberOfBlocks(index);
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		BloomTuple *itup, *itupPtr, *itupEnd;
+ 
+ 		buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
+ 									RBM_NORMAL, info->strategy);
+ 
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		GenericXLogStart(index);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		if (BloomPageIsDeleted(page))
+ 		{
+ 			UnlockReleaseBuffer(buffer);
+ 			CHECK_FOR_INTERRUPTS();
+ 			continue;
+ 		}
+ 
+ 		/* Iterate over the tuples */
+ 		itup = BloomPageGetTuple(&state, page, 1);
+ 		itupPtr = BloomPageGetTuple(&state, page, 1);
+ 		itupEnd = BloomPageGetTuple(&state, page, BloomPageGetMaxOffset(page) + 1);
+ 		while (itup < itupEnd)
+ 		{
+ 			/* Do we have to delete this tuple? */
+ 			if (callback(&itup->heapPtr, callback_state))
+ 			{
+ 				stats->tuples_removed += 1;
+ 				BloomPageGetOpaque(page)->maxoff--;
+ 			} 
+ 			else 
+ 			{
+ 				if (itupPtr != itup)
+ 				{
+ 					/*
+ 					 * If we already delete something before, we have to move
+ 					 * this tuple backward.
+ 					 */
+ 					memmove((Pointer)itupPtr, (Pointer)itup,
+ 							state.sizeOfBloomTuple);
+ 				}
+ 				stats->num_index_tuples++;
+ 				itupPtr = BloomPageGetNextTuple(&state, itupPtr);
+ 			}
+ 
+ 			itup = BloomPageGetNextTuple(&state, itup);
+ 		}
+ 
+ 		/* Did we delete something? */
+ 		if (itupPtr != itup)
+ 		{
+ 			/* Is it empty page now? */
+ 			if (itupPtr == BloomPageGetData(page))
+ 				BloomPageSetDeleted(page);
+ 			/* Adjust pg_lower */
+ 			((PageHeader) page)->pd_lower = (Pointer)itupPtr - page;
+ 			/* Finish WAL-logging */
+ 			GenericXLogFinish();
+ 		}
+ 		else
+ 		{
+ 			/* Didn't change anything: abort WAL-logging */
+ 			GenericXLogAbort();
+ 		}
+ 
+ 		if (!BloomPageIsDeleted(page) && 
+ 				BloomPageGetFreeSpace(&state, page) > state.sizeOfBloomTuple && 
+ 				countPage < BloomMetaBlockN)
+ 			notFullPage[countPage++] = blkno;
+ 		UnlockReleaseBuffer(buffer);
+ 		CHECK_FOR_INTERRUPTS();
+ 	}
+ 
+ 	if (countPage > 0)
+ 	{
+ 		BloomMetaPageData	*metaData;
+ 
+ 		buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 		GenericXLogStart(index);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		metaData = BloomPageGetMeta(page);
+ 		memcpy(metaData->notFullPage, notFullPage, sizeof(FreeBlockNumberArray));
+ 		metaData->nStart=0;
+ 		metaData->nEnd = countPage;
+ 
+ 		GenericXLogFinish();
+ 		UnlockReleaseBuffer(buffer);
+ 	}
+ 
+ 	return stats;
+ }
+ 
+ IndexBulkDeleteResult *
+ blvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
+ {
+ 	Relation	index = info->index;
+ 	BlockNumber npages,
+ 				blkno;
+ 	BlockNumber	totFreePages;
+ 
+ 	if (info->analyze_only)
+ 		return stats;
+ 
+ 	if (stats == NULL)
+ 		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+ 
+ 	/*
+ 	 * Iterate over the pages: insert deleted pages into FSM and collect
+ 	 * statistics.
+ 	 */
+ 	npages = RelationGetNumberOfBlocks(index);
+ 	totFreePages = 0;
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		Buffer		buffer;
+ 		Page		page;
+ 
+ 		vacuum_delay_point();
+ 
+ 		buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
+ 									RBM_NORMAL, info->strategy);
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 		page = (Page) BufferGetPage(buffer);
+ 
+ 		if (BloomPageIsDeleted(page))
+ 		{
+ 			RecordFreeIndexPage(index, blkno);
+ 			totFreePages++;
+ 		}
+ 		else
+ 		{
+ 			stats->num_index_tuples += BloomPageGetMaxOffset(page);
+ 			stats->estimated_count += BloomPageGetMaxOffset(page);
+ 		}
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 	}
+ 
+ 	IndexFreeSpaceMapVacuum(info->index);
+ 	stats->pages_free = totFreePages;
+ 	stats->num_pages = RelationGetNumberOfBlocks(index);
+ 
+ 	return stats;
+ }
diff --git a/contrib/bloom/blvalidate.c b/contrib/bloom/blvalidate.c
new file mode 100644
index ...5344b81
*** a/contrib/bloom/blvalidate.c
--- b/contrib/bloom/blvalidate.c
***************
*** 0 ****
--- 1,220 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blvalidate.c
+  *	  Opclass validator for bloom.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blvalidate.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/amvalidate.h"
+ #include "access/htup_details.h"
+ #include "catalog/pg_amop.h"
+ #include "catalog/pg_amproc.h"
+ #include "catalog/pg_opclass.h"
+ #include "catalog/pg_opfamily.h"
+ #include "catalog/pg_type.h"
+ #include "utils/builtins.h"
+ #include "utils/lsyscache.h"
+ #include "utils/syscache.h"
+ 
+ #include "bloom.h"
+ 
+ /*
+  * Validator for a bloom opclass.
+  */
+ bool
+ blvalidate(Oid opclassoid)
+ {
+ 	bool		result = true;
+ 	HeapTuple	classtup;
+ 	Form_pg_opclass classform;
+ 	Oid			opfamilyoid;
+ 	Oid			opcintype;
+ 	Oid			opckeytype;
+ 	char	   *opclassname;
+ 	HeapTuple	familytup;
+ 	Form_pg_opfamily familyform;
+ 	char	   *opfamilyname;
+ 	CatCList   *proclist,
+ 			   *oprlist;
+ 	List	   *grouplist;
+ 	OpFamilyOpFuncGroup *opclassgroup;
+ 	int			i;
+ 	ListCell   *lc;
+ 
+ 	/* Fetch opclass information */
+ 	classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
+ 	if (!HeapTupleIsValid(classtup))
+ 		elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
+ 	classform = (Form_pg_opclass) GETSTRUCT(classtup);
+ 
+ 	opfamilyoid = classform->opcfamily;
+ 	opcintype = classform->opcintype;
+ 	opckeytype = classform->opckeytype;
+ 	if (!OidIsValid(opckeytype))
+ 		opckeytype = opcintype;
+ 	opclassname = NameStr(classform->opcname);
+ 
+ 	/* Fetch opfamily information */
+ 	familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
+ 	if (!HeapTupleIsValid(familytup))
+ 		elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
+ 	familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
+ 
+ 	opfamilyname = NameStr(familyform->opfname);
+ 
+ 	/* Fetch all operators and support functions of the opfamily */
+ 	oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
+ 	proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
+ 
+ 	/* Check individual support functions */
+ 	for (i = 0; i < proclist->n_members; i++)
+ 	{
+ 		HeapTuple	proctup = &proclist->members[i]->tuple;
+ 		Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
+ 		bool		ok;
+ 
+ 		/*
+ 		 * All bloom support functions should be registered with matching
+ 		 * left/right types
+ 		 */
+ 		if (procform->amproclefttype != procform->amprocrighttype)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains support procedure %s with cross-type registration",
+ 							opfamilyname,
+ 							format_procedure(procform->amproc))));
+ 			result = false;
+ 		}
+ 
+ 		/*
+ 		 * We can't check signatures except within the specific opclass, since
+ 		 * we need to know the associated opckeytype in many cases.
+ 		 */
+ 		if (procform->amproclefttype != opcintype)
+ 			continue;
+ 
+ 		/* Check procedure numbers and function signatures */
+ 		switch (procform->amprocnum)
+ 		{
+ 			case BLOOM_HASH_PROC:
+ 				ok = check_amproc_signature(procform->amproc, INT4OID, false,
+ 											1, 1, opckeytype);
+ 				break;
+ 			default:
+ 				ereport(INFO,
+ 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 						 errmsg("bloom opfamily %s contains function %s with invalid support number %d",
+ 								opfamilyname,
+ 								format_procedure(procform->amproc),
+ 								procform->amprocnum)));
+ 				result = false;
+ 				continue;		/* don't want additional message */
+ 		}
+ 
+ 		if (!ok)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("gist opfamily %s contains function %s with wrong signature for support number %d",
+ 							opfamilyname,
+ 							format_procedure(procform->amproc),
+ 							procform->amprocnum)));
+ 			result = false;
+ 		}
+ 	}
+ 
+ 	/* Check individual operators */
+ 	for (i = 0; i < oprlist->n_members; i++)
+ 	{
+ 		HeapTuple	oprtup = &oprlist->members[i]->tuple;
+ 		Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
+ 
+ 		/* Check it's allowed strategy for bloom */
+ 		if (oprform->amopstrategy < 1 || 
+ 			oprform->amopstrategy > BLOOM_NSTRATEGIES)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains operator %s with invalid strategy number %d",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr),
+ 							oprform->amopstrategy)));
+ 			result = false;
+ 		}
+ 
+ 		/* bloom doesn't support ORDER BY operators */
+ 		if (oprform->amoppurpose != AMOP_SEARCH ||
+ 			OidIsValid(oprform->amopsortfamily))
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains invalid ORDER BY specification for operator %s",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr))));
+ 			result = false;
+ 		}
+ 
+ 		/* Check operator signature --- same for all bloom strategies */
+ 		if (!check_amop_signature(oprform->amopopr, BOOLOID,
+ 								  oprform->amoplefttype,
+ 								  oprform->amoprighttype))
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains operator %s with wrong signature",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr))));
+ 			result = false;
+ 		}
+ 	}
+ 
+ 	/* Now check for inconsistent groups of operators/functions */
+ 	grouplist = identify_opfamily_groups(oprlist, proclist);
+ 	opclassgroup = NULL;
+ 	foreach(lc, grouplist)
+ 	{
+ 		OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
+ 
+ 		/* Remember the group exactly matching the test opclass */
+ 		if (thisgroup->lefttype == opcintype &&
+ 			thisgroup->righttype == opcintype)
+ 			opclassgroup = thisgroup;
+ 
+ 		/*
+ 		 * There is not a lot we can do to check the operator sets, since each
+ 		 * bloom opclass is more or less a law unto itself, and some contain
+ 		 * only operators that are binary-compatible with the opclass datatype
+ 		 * (meaning that empty operator sets can be OK).  That case also means
+ 		 * that we shouldn't insist on nonempty function sets except for the
+ 		 * opclass's own group.
+ 		 */
+ 	}
+ 
+ 	/* Check that the originally-named opclass is complete */
+ 	for (i = 1; i <= BLOOM_NPROC; i++)
+ 	{
+ 		if (opclassgroup &&
+ 			(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
+ 			continue;			/* got it */
+ 		ereport(INFO,
+ 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 				 errmsg("bloom opclass %s is missing support function %d",
+ 						opclassname, i)));
+ 		result = false;
+ 	}
+ 
+ 	ReleaseCatCacheList(proclist);
+ 	ReleaseCatCacheList(oprlist);
+ 	ReleaseSysCache(familytup);
+ 	ReleaseSysCache(classtup);
+ 
+ 	return result;
+ }
diff --git a/contrib/bloom/data/data b/contrib/bloom/data/data
new file mode 100644
index ...eacf3e7
*** a/contrib/bloom/data/data
--- b/contrib/bloom/data/data
***************
*** 0 ****
--- 1,10000 ----
+ 739	3
+ 475	9
+ 45	6
+ 433	1
+ 948	8
+ 926	8
+ 397	7
+ 980	4
+ 212	5
+ 522	9
+ 74	8
+ 77	4
+ 378	9
+ 575	3
+ 625	2
+ 407	4
+ 509	9
+ 252	6
+ 487	7
+ 656	4
+ 485	6
+ 275	9
+ 285	3
+ 277	5
+ 804	8
+ 424	9
+ 553	5
+ 245	9
+ 384	8
+ 202	0
+ 43	6
+ 374	6
+ 490	6
+ 105	10
+ 311	8
+ 411	8
+ 343	7
+ 678	6
+ 942	10
+ 126	7
+ 755	6
+ 625	3
+ 52	9
+ 239	4
+ 690	4
+ 445	7
+ 49	8
+ 285	5
+ 445	4
+ 516	8
+ 151	9
+ 553	5
+ 613	2
+ 123	6
+ 187	2
+ 301	9
+ 800	9
+ 250	9
+ 796	5
+ 288	5
+ 930	7
+ 219	10
+ 553	5
+ 518	10
+ 893	0
+ 754	0
+ 960	3
+ 538	6
+ 537	7
+ 127	7
+ 910	4
+ 666	7
+ 354	9
+ 562	1
+ 405	8
+ 635	3
+ 583	9
+ 313	1
+ 358	8
+ 133	3
+ 864	9
+ 296	8
+ 193	8
+ 396	7
+ 495	5
+ 454	4
+ 952	1
+ 115	3
+ 36	7
+ 455	4
+ 527	1
+ 775	1
+ 945	1
+ 246	3
+ 920	4
+ 554	8
+ 267	8
+ 608	5
+ 684	0
+ 190	2
+ 527	6
+ 584	5
+ 764	7
+ 785	8
+ 376	2
+ 240	9
+ 330	0
+ 13	3
+ 103	3
+ 578	0
+ 639	1
+ 807	9
+ 982	4
+ 365	7
+ 418	6
+ 844	9
+ 199	4
+ 425	10
+ 127	2
+ 762	5
+ 450	0
+ 406	8
+ 17	4
+ 55	1
+ 678	6
+ 143	3
+ 764	9
+ 222	7
+ 364	6
+ 411	8
+ 142	3
+ 728	3
+ 684	2
+ 303	8
+ 363	1
+ 314	8
+ 67	7
+ 592	1
+ 139	6
+ 204	8
+ 279	3
+ 133	0
+ 296	4
+ 788	7
+ 942	2
+ 441	1
+ 454	2
+ 424	1
+ 322	7
+ 949	7
+ 793	3
+ 497	9
+ 984	1
+ 944	1
+ 736	1
+ 940	0
+ 494	1
+ 57	8
+ 429	8
+ 449	4
+ 44	9
+ 454	5
+ 60	9
+ 636	4
+ 606	6
+ 67	4
+ 848	6
+ 259	8
+ 654	2
+ 955	4
+ 351	9
+ 405	8
+ 968	5
+ 634	4
+ 308	1
+ 767	4
+ 974	2
+ 850	0
+ 99	5
+ 416	7
+ 71	5
+ 103	9
+ 48	4
+ 750	7
+ 565	7
+ 92	9
+ 599	5
+ 760	6
+ 960	4
+ 964	3
+ 478	7
+ 620	5
+ 953	5
+ 485	1
+ 957	9
+ 756	0
+ 383	9
+ 946	4
+ 222	7
+ 133	8
+ 401	2
+ 702	0
+ 723	5
+ 568	7
+ 857	5
+ 951	3
+ 264	6
+ 786	2
+ 42	3
+ 268	10
+ 172	0
+ 26	6
+ 884	10
+ 986	1
+ 667	1
+ 893	1
+ 344	6
+ 68	1
+ 58	6
+ 750	9
+ 168	7
+ 249	4
+ 273	0
+ 649	3
+ 306	9
+ 314	5
+ 942	3
+ 33	8
+ 311	0
+ 932	10
+ 138	8
+ 47	5
+ 420	1
+ 550	5
+ 751	3
+ 392	9
+ 1	6
+ 351	3
+ 677	10
+ 588	10
+ 917	9
+ 461	9
+ 242	5
+ 685	6
+ 514	6
+ 531	7
+ 442	6
+ 135	9
+ 693	7
+ 341	4
+ 984	7
+ 362	10
+ 375	7
+ 259	1
+ 713	8
+ 35	6
+ 750	5
+ 489	10
+ 991	2
+ 544	5
+ 791	1
+ 156	2
+ 653	3
+ 96	3
+ 976	4
+ 789	10
+ 170	2
+ 946	5
+ 865	2
+ 597	6
+ 53	6
+ 209	8
+ 128	7
+ 794	1
+ 871	3
+ 623	7
+ 413	8
+ 895	1
+ 70	10
+ 411	0
+ 428	2
+ 6	6
+ 352	10
+ 143	2
+ 156	7
+ 795	2
+ 372	0
+ 11	5
+ 701	8
+ 619	6
+ 142	2
+ 233	6
+ 20	1
+ 621	1
+ 118	0
+ 136	5
+ 232	1
+ 145	6
+ 93	3
+ 800	2
+ 28	6
+ 457	4
+ 598	5
+ 900	3
+ 273	5
+ 870	4
+ 760	1
+ 970	8
+ 231	6
+ 871	3
+ 622	0
+ 895	9
+ 148	0
+ 439	2
+ 328	2
+ 489	4
+ 834	9
+ 756	4
+ 415	7
+ 730	7
+ 175	6
+ 102	9
+ 703	1
+ 715	9
+ 662	6
+ 283	3
+ 592	2
+ 139	7
+ 218	6
+ 981	5
+ 816	5
+ 902	7
+ 417	7
+ 82	8
+ 314	8
+ 519	5
+ 413	6
+ 424	1
+ 693	1
+ 50	4
+ 725	3
+ 639	3
+ 511	8
+ 58	7
+ 356	0
+ 275	2
+ 510	2
+ 822	9
+ 835	9
+ 759	1
+ 717	3
+ 639	1
+ 899	1
+ 246	6
+ 202	3
+ 946	9
+ 629	6
+ 245	1
+ 363	3
+ 870	7
+ 342	1
+ 891	9
+ 322	7
+ 778	2
+ 617	5
+ 307	3
+ 814	9
+ 463	7
+ 8	7
+ 304	2
+ 5	2
+ 138	6
+ 835	4
+ 774	2
+ 685	6
+ 916	0
+ 789	8
+ 878	1
+ 519	7
+ 269	1
+ 193	6
+ 470	0
+ 522	9
+ 720	5
+ 643	0
+ 741	6
+ 275	9
+ 282	1
+ 261	1
+ 306	9
+ 701	2
+ 973	5
+ 28	9
+ 602	5
+ 507	9
+ 683	7
+ 448	2
+ 708	10
+ 88	4
+ 501	7
+ 453	2
+ 379	7
+ 120	7
+ 836	4
+ 717	1
+ 327	4
+ 365	3
+ 908	4
+ 151	5
+ 940	7
+ 381	6
+ 359	8
+ 777	1
+ 799	9
+ 495	3
+ 595	9
+ 542	10
+ 675	7
+ 634	5
+ 44	4
+ 654	4
+ 769	0
+ 671	7
+ 411	8
+ 187	4
+ 481	6
+ 974	8
+ 398	8
+ 907	2
+ 615	4
+ 497	2
+ 349	0
+ 183	0
+ 701	8
+ 535	7
+ 169	2
+ 116	9
+ 208	8
+ 615	6
+ 610	8
+ 970	1
+ 371	9
+ 931	8
+ 695	8
+ 966	3
+ 239	5
+ 520	6
+ 502	7
+ 612	2
+ 520	1
+ 948	7
+ 337	1
+ 627	5
+ 852	2
+ 165	5
+ 45	1
+ 554	4
+ 79	5
+ 185	8
+ 323	2
+ 84	6
+ 613	6
+ 151	1
+ 306	8
+ 318	8
+ 911	3
+ 516	2
+ 331	1
+ 793	2
+ 385	10
+ 646	4
+ 92	2
+ 846	2
+ 686	0
+ 945	0
+ 181	0
+ 572	8
+ 633	7
+ 909	9
+ 486	2
+ 766	4
+ 493	3
+ 645	8
+ 424	4
+ 8	8
+ 395	7
+ 240	5
+ 855	1
+ 659	5
+ 117	6
+ 551	3
+ 634	1
+ 93	3
+ 846	0
+ 206	3
+ 228	10
+ 730	7
+ 253	4
+ 546	7
+ 813	6
+ 487	2
+ 209	7
+ 696	1
+ 814	4
+ 605	9
+ 959	2
+ 230	6
+ 278	3
+ 860	1
+ 324	1
+ 457	6
+ 37	2
+ 273	3
+ 561	8
+ 968	4
+ 373	5
+ 582	6
+ 183	3
+ 646	10
+ 633	3
+ 928	6
+ 407	2
+ 185	7
+ 480	0
+ 810	8
+ 111	3
+ 355	1
+ 453	6
+ 439	0
+ 447	4
+ 388	8
+ 862	10
+ 402	0
+ 247	0
+ 42	9
+ 299	10
+ 472	7
+ 127	7
+ 392	6
+ 702	2
+ 410	8
+ 468	8
+ 960	9
+ 394	4
+ 935	8
+ 806	3
+ 661	7
+ 292	1
+ 714	5
+ 111	8
+ 419	4
+ 725	9
+ 117	9
+ 548	5
+ 460	2
+ 711	9
+ 62	2
+ 636	0
+ 99	0
+ 421	0
+ 870	2
+ 357	5
+ 896	6
+ 594	6
+ 189	7
+ 365	6
+ 116	1
+ 499	2
+ 943	0
+ 742	4
+ 297	5
+ 273	4
+ 631	9
+ 382	7
+ 938	8
+ 765	8
+ 30	1
+ 338	9
+ 771	9
+ 535	10
+ 637	9
+ 568	8
+ 990	1
+ 986	9
+ 114	7
+ 335	4
+ 181	6
+ 770	8
+ 517	2
+ 543	5
+ 954	3
+ 262	10
+ 430	6
+ 910	2
+ 531	4
+ 160	2
+ 345	7
+ 921	3
+ 795	9
+ 267	9
+ 635	6
+ 319	8
+ 211	1
+ 628	7
+ 240	2
+ 182	2
+ 479	4
+ 179	9
+ 43	1
+ 110	6
+ 535	3
+ 743	9
+ 999	7
+ 214	8
+ 571	5
+ 702	2
+ 85	0
+ 22	3
+ 111	6
+ 23	4
+ 821	2
+ 546	3
+ 648	7
+ 210	7
+ 814	3
+ 267	3
+ 590	0
+ 228	6
+ 674	4
+ 382	2
+ 924	1
+ 450	0
+ 106	5
+ 304	2
+ 121	3
+ 568	9
+ 532	1
+ 243	2
+ 839	5
+ 872	7
+ 772	1
+ 2	4
+ 148	2
+ 950	8
+ 673	3
+ 66	6
+ 416	5
+ 606	5
+ 987	9
+ 739	1
+ 237	3
+ 51	8
+ 422	3
+ 949	3
+ 746	8
+ 914	5
+ 959	9
+ 880	1
+ 146	8
+ 929	8
+ 163	10
+ 416	6
+ 511	0
+ 102	5
+ 932	8
+ 606	2
+ 149	7
+ 939	6
+ 951	9
+ 831	7
+ 710	7
+ 215	7
+ 662	1
+ 777	8
+ 926	7
+ 627	1
+ 701	0
+ 668	2
+ 66	8
+ 709	10
+ 611	3
+ 168	8
+ 973	1
+ 330	9
+ 996	2
+ 620	7
+ 907	8
+ 375	6
+ 931	2
+ 377	9
+ 857	0
+ 945	6
+ 49	6
+ 769	1
+ 384	5
+ 113	10
+ 794	3
+ 756	8
+ 389	1
+ 690	4
+ 248	3
+ 90	2
+ 146	5
+ 724	1
+ 616	1
+ 933	5
+ 106	9
+ 31	2
+ 492	8
+ 270	9
+ 279	4
+ 872	1
+ 664	6
+ 840	1
+ 713	5
+ 438	10
+ 841	5
+ 116	10
+ 994	8
+ 63	6
+ 942	10
+ 83	0
+ 874	1
+ 203	4
+ 914	5
+ 242	2
+ 856	1
+ 265	5
+ 742	1
+ 573	5
+ 635	0
+ 416	5
+ 540	5
+ 462	5
+ 373	5
+ 143	3
+ 520	2
+ 363	4
+ 340	6
+ 760	3
+ 40	0
+ 446	9
+ 117	7
+ 416	9
+ 816	10
+ 313	5
+ 0	7
+ 927	5
+ 261	4
+ 74	6
+ 914	2
+ 949	4
+ 443	3
+ 829	8
+ 879	6
+ 37	9
+ 591	5
+ 814	7
+ 195	2
+ 566	0
+ 218	9
+ 462	2
+ 608	4
+ 759	9
+ 778	8
+ 504	7
+ 49	5
+ 127	5
+ 765	10
+ 276	6
+ 544	3
+ 562	1
+ 797	4
+ 843	10
+ 605	4
+ 2	8
+ 288	5
+ 42	9
+ 853	8
+ 765	6
+ 633	3
+ 324	7
+ 722	5
+ 175	5
+ 406	5
+ 130	9
+ 765	7
+ 85	6
+ 68	9
+ 553	7
+ 337	6
+ 497	6
+ 19	5
+ 520	9
+ 340	3
+ 504	10
+ 554	8
+ 656	3
+ 279	8
+ 763	7
+ 283	9
+ 634	0
+ 585	7
+ 609	7
+ 647	2
+ 326	10
+ 717	8
+ 608	7
+ 362	1
+ 608	7
+ 413	1
+ 676	10
+ 940	3
+ 244	2
+ 163	0
+ 903	4
+ 899	5
+ 494	5
+ 256	1
+ 136	9
+ 264	5
+ 886	10
+ 285	5
+ 717	6
+ 621	3
+ 349	0
+ 436	0
+ 2	4
+ 356	2
+ 595	5
+ 251	5
+ 965	2
+ 34	5
+ 633	3
+ 562	8
+ 192	8
+ 231	1
+ 807	5
+ 571	5
+ 163	2
+ 848	5
+ 226	3
+ 536	2
+ 661	9
+ 473	3
+ 412	7
+ 753	4
+ 874	8
+ 837	5
+ 77	4
+ 277	3
+ 225	5
+ 347	0
+ 24	9
+ 555	2
+ 109	4
+ 699	3
+ 688	2
+ 563	3
+ 128	0
+ 604	5
+ 759	4
+ 919	6
+ 143	8
+ 141	2
+ 154	4
+ 488	4
+ 926	8
+ 410	10
+ 752	10
+ 137	9
+ 369	8
+ 197	1
+ 72	8
+ 405	2
+ 795	0
+ 741	6
+ 365	7
+ 187	5
+ 415	3
+ 728	6
+ 745	2
+ 948	7
+ 51	4
+ 621	8
+ 324	8
+ 665	7
+ 595	9
+ 750	7
+ 622	2
+ 867	4
+ 164	6
+ 971	5
+ 267	2
+ 38	7
+ 485	8
+ 251	2
+ 982	2
+ 902	0
+ 556	5
+ 836	9
+ 282	5
+ 573	9
+ 364	3
+ 543	10
+ 477	4
+ 403	6
+ 18	4
+ 171	3
+ 531	2
+ 966	0
+ 974	2
+ 247	10
+ 415	1
+ 988	10
+ 672	8
+ 851	10
+ 325	4
+ 830	7
+ 746	4
+ 675	2
+ 784	1
+ 865	8
+ 450	0
+ 86	10
+ 244	1
+ 998	2
+ 269	2
+ 173	7
+ 394	2
+ 655	1
+ 986	5
+ 20	3
+ 930	8
+ 0	7
+ 224	7
+ 900	0
+ 752	8
+ 809	2
+ 800	9
+ 184	0
+ 947	2
+ 261	2
+ 427	4
+ 899	8
+ 596	6
+ 887	6
+ 60	9
+ 894	10
+ 757	9
+ 667	10
+ 569	6
+ 987	3
+ 331	8
+ 524	1
+ 691	7
+ 174	6
+ 891	4
+ 854	3
+ 870	8
+ 139	5
+ 307	0
+ 48	4
+ 933	9
+ 358	7
+ 836	0
+ 670	4
+ 591	7
+ 726	9
+ 454	3
+ 53	1
+ 959	2
+ 783	8
+ 663	6
+ 168	5
+ 389	3
+ 999	7
+ 334	0
+ 64	3
+ 989	4
+ 957	8
+ 447	6
+ 231	0
+ 285	10
+ 960	7
+ 208	0
+ 883	2
+ 240	7
+ 16	9
+ 302	2
+ 435	7
+ 490	4
+ 388	8
+ 481	5
+ 91	5
+ 874	0
+ 296	3
+ 675	5
+ 359	10
+ 484	3
+ 698	7
+ 332	6
+ 858	6
+ 247	9
+ 475	5
+ 57	9
+ 241	5
+ 344	6
+ 371	8
+ 81	5
+ 296	10
+ 509	6
+ 277	2
+ 120	6
+ 143	6
+ 955	8
+ 296	3
+ 421	2
+ 860	7
+ 28	3
+ 217	1
+ 244	5
+ 632	6
+ 87	0
+ 414	2
+ 465	7
+ 123	10
+ 303	4
+ 158	4
+ 36	3
+ 27	10
+ 142	3
+ 278	6
+ 476	1
+ 231	5
+ 472	4
+ 588	7
+ 907	2
+ 305	10
+ 223	7
+ 161	7
+ 428	3
+ 662	7
+ 684	8
+ 154	7
+ 121	2
+ 711	3
+ 503	10
+ 826	10
+ 127	1
+ 483	6
+ 506	1
+ 316	4
+ 292	6
+ 407	5
+ 339	6
+ 203	8
+ 853	9
+ 499	5
+ 684	7
+ 257	8
+ 833	10
+ 68	3
+ 958	9
+ 316	1
+ 950	8
+ 685	5
+ 870	0
+ 869	2
+ 622	3
+ 676	10
+ 844	9
+ 729	7
+ 743	2
+ 234	4
+ 881	5
+ 233	7
+ 460	3
+ 51	4
+ 193	4
+ 503	1
+ 165	2
+ 600	0
+ 189	5
+ 197	8
+ 745	9
+ 773	6
+ 752	5
+ 285	5
+ 730	5
+ 923	6
+ 10	2
+ 324	5
+ 455	4
+ 888	6
+ 741	4
+ 792	9
+ 579	4
+ 942	8
+ 862	1
+ 580	6
+ 12	4
+ 196	8
+ 854	5
+ 259	6
+ 1	2
+ 195	0
+ 336	5
+ 481	8
+ 894	4
+ 440	6
+ 760	2
+ 542	3
+ 625	5
+ 107	5
+ 622	7
+ 94	6
+ 40	3
+ 397	9
+ 771	7
+ 479	8
+ 837	7
+ 783	2
+ 192	3
+ 964	1
+ 633	4
+ 721	4
+ 637	3
+ 732	3
+ 746	8
+ 749	4
+ 527	8
+ 2	6
+ 133	4
+ 462	9
+ 54	9
+ 677	9
+ 613	5
+ 64	8
+ 725	0
+ 891	4
+ 433	6
+ 751	1
+ 876	5
+ 332	6
+ 324	1
+ 990	9
+ 925	10
+ 418	1
+ 390	9
+ 962	4
+ 820	6
+ 335	4
+ 99	4
+ 239	8
+ 427	1
+ 182	9
+ 743	9
+ 930	6
+ 418	3
+ 241	7
+ 344	2
+ 593	3
+ 223	0
+ 326	6
+ 891	3
+ 58	7
+ 928	4
+ 145	0
+ 792	4
+ 851	2
+ 514	0
+ 80	3
+ 967	0
+ 875	4
+ 272	1
+ 126	6
+ 347	7
+ 884	6
+ 730	2
+ 184	6
+ 498	2
+ 333	4
+ 635	5
+ 453	4
+ 861	3
+ 647	4
+ 338	7
+ 632	3
+ 736	5
+ 689	0
+ 624	8
+ 623	10
+ 534	5
+ 541	3
+ 717	7
+ 885	2
+ 967	2
+ 641	6
+ 696	1
+ 29	6
+ 399	7
+ 933	7
+ 403	6
+ 41	1
+ 73	7
+ 147	7
+ 546	8
+ 668	1
+ 278	2
+ 344	10
+ 934	2
+ 209	9
+ 447	8
+ 503	1
+ 944	5
+ 700	3
+ 208	6
+ 79	6
+ 198	1
+ 750	3
+ 851	9
+ 967	4
+ 668	6
+ 477	9
+ 843	8
+ 940	8
+ 51	1
+ 678	5
+ 999	2
+ 641	9
+ 713	3
+ 285	9
+ 974	4
+ 532	2
+ 485	3
+ 442	3
+ 179	4
+ 733	8
+ 44	2
+ 792	9
+ 32	7
+ 664	1
+ 880	3
+ 581	9
+ 523	2
+ 822	2
+ 563	1
+ 157	5
+ 471	7
+ 709	10
+ 971	2
+ 292	2
+ 561	0
+ 997	6
+ 236	8
+ 491	3
+ 521	2
+ 351	4
+ 498	9
+ 281	0
+ 153	1
+ 258	7
+ 209	4
+ 253	7
+ 105	10
+ 636	1
+ 113	9
+ 227	7
+ 954	2
+ 278	2
+ 14	8
+ 459	5
+ 926	8
+ 937	4
+ 742	2
+ 446	9
+ 320	7
+ 611	5
+ 120	9
+ 210	2
+ 827	8
+ 301	9
+ 775	5
+ 614	7
+ 753	9
+ 918	8
+ 663	4
+ 302	6
+ 187	2
+ 13	9
+ 457	5
+ 824	8
+ 163	4
+ 307	3
+ 300	5
+ 508	1
+ 363	8
+ 67	1
+ 338	7
+ 866	1
+ 573	8
+ 858	2
+ 161	2
+ 824	3
+ 399	8
+ 277	9
+ 295	1
+ 633	5
+ 536	9
+ 742	8
+ 456	3
+ 963	8
+ 61	0
+ 956	4
+ 710	8
+ 490	3
+ 606	3
+ 519	8
+ 508	3
+ 116	9
+ 179	4
+ 762	5
+ 494	4
+ 934	0
+ 335	7
+ 867	8
+ 926	8
+ 610	10
+ 859	6
+ 386	6
+ 389	9
+ 852	10
+ 224	4
+ 763	7
+ 713	9
+ 638	9
+ 272	4
+ 367	8
+ 796	3
+ 796	1
+ 977	7
+ 921	9
+ 493	5
+ 890	4
+ 98	3
+ 921	5
+ 152	8
+ 482	4
+ 143	2
+ 108	9
+ 124	7
+ 750	4
+ 147	1
+ 162	9
+ 418	10
+ 73	4
+ 622	10
+ 298	1
+ 526	2
+ 466	6
+ 464	4
+ 111	6
+ 159	6
+ 992	3
+ 837	1
+ 159	10
+ 847	9
+ 357	10
+ 26	5
+ 937	4
+ 478	0
+ 839	1
+ 5	1
+ 214	5
+ 325	7
+ 156	8
+ 66	3
+ 405	2
+ 859	4
+ 527	7
+ 498	7
+ 658	3
+ 595	0
+ 339	6
+ 535	3
+ 65	0
+ 286	9
+ 112	3
+ 41	3
+ 823	4
+ 6	10
+ 154	1
+ 245	6
+ 295	1
+ 957	8
+ 800	5
+ 508	5
+ 801	1
+ 473	1
+ 723	0
+ 415	8
+ 21	7
+ 691	1
+ 993	7
+ 460	8
+ 97	5
+ 795	3
+ 536	0
+ 811	8
+ 144	8
+ 654	9
+ 224	2
+ 403	0
+ 263	9
+ 165	10
+ 884	6
+ 774	9
+ 282	5
+ 39	3
+ 197	5
+ 91	3
+ 964	9
+ 546	5
+ 926	4
+ 332	1
+ 127	10
+ 15	4
+ 146	4
+ 376	4
+ 293	5
+ 396	2
+ 120	2
+ 83	4
+ 636	1
+ 677	8
+ 620	8
+ 128	6
+ 655	7
+ 84	6
+ 32	4
+ 651	2
+ 400	7
+ 510	5
+ 83	9
+ 957	4
+ 426	4
+ 554	5
+ 523	6
+ 949	2
+ 758	6
+ 992	4
+ 395	1
+ 962	0
+ 794	0
+ 630	8
+ 461	3
+ 984	9
+ 947	5
+ 408	0
+ 380	4
+ 407	8
+ 717	10
+ 352	2
+ 598	3
+ 399	4
+ 927	4
+ 734	3
+ 510	7
+ 371	3
+ 742	0
+ 129	2
+ 283	1
+ 63	2
+ 608	5
+ 261	10
+ 835	7
+ 793	6
+ 628	1
+ 793	2
+ 446	2
+ 582	4
+ 583	3
+ 695	1
+ 13	1
+ 397	8
+ 68	5
+ 957	4
+ 641	0
+ 582	2
+ 491	8
+ 235	3
+ 510	0
+ 879	1
+ 173	7
+ 365	6
+ 863	9
+ 992	4
+ 264	7
+ 540	3
+ 754	9
+ 32	8
+ 464	10
+ 174	1
+ 9	8
+ 353	5
+ 598	6
+ 827	1
+ 616	7
+ 247	8
+ 377	6
+ 407	2
+ 558	4
+ 686	8
+ 86	2
+ 99	8
+ 163	1
+ 662	6
+ 120	8
+ 731	1
+ 591	1
+ 630	2
+ 671	5
+ 298	3
+ 162	5
+ 75	5
+ 155	5
+ 779	7
+ 880	5
+ 535	10
+ 691	6
+ 806	9
+ 764	5
+ 480	9
+ 303	2
+ 13	9
+ 294	6
+ 84	10
+ 100	4
+ 252	3
+ 926	3
+ 801	1
+ 808	6
+ 794	7
+ 45	3
+ 655	7
+ 963	5
+ 589	7
+ 929	1
+ 611	2
+ 279	6
+ 127	6
+ 267	2
+ 538	4
+ 592	8
+ 629	5
+ 117	4
+ 599	9
+ 10	4
+ 614	1
+ 722	3
+ 790	7
+ 730	4
+ 413	7
+ 447	0
+ 891	7
+ 648	0
+ 299	9
+ 228	8
+ 282	8
+ 627	9
+ 338	7
+ 340	9
+ 669	3
+ 330	3
+ 404	1
+ 552	2
+ 738	3
+ 574	2
+ 941	0
+ 174	8
+ 747	8
+ 849	0
+ 738	1
+ 884	0
+ 897	5
+ 931	2
+ 256	3
+ 173	9
+ 621	5
+ 209	0
+ 556	8
+ 220	3
+ 43	8
+ 444	10
+ 815	6
+ 816	6
+ 441	7
+ 609	2
+ 742	5
+ 199	6
+ 4	1
+ 875	3
+ 400	0
+ 185	0
+ 551	4
+ 46	1
+ 155	3
+ 400	2
+ 60	8
+ 183	9
+ 463	10
+ 436	9
+ 665	0
+ 82	4
+ 538	3
+ 47	5
+ 410	9
+ 802	8
+ 970	10
+ 832	5
+ 381	9
+ 627	5
+ 145	0
+ 734	2
+ 872	9
+ 79	3
+ 916	5
+ 238	6
+ 560	3
+ 988	1
+ 602	0
+ 639	0
+ 956	4
+ 823	9
+ 429	7
+ 446	8
+ 533	1
+ 346	7
+ 101	1
+ 883	10
+ 997	10
+ 307	9
+ 477	5
+ 495	0
+ 865	5
+ 135	5
+ 517	8
+ 479	5
+ 215	3
+ 399	6
+ 957	8
+ 454	5
+ 919	8
+ 168	0
+ 880	1
+ 992	9
+ 13	3
+ 791	5
+ 844	3
+ 527	7
+ 768	7
+ 176	3
+ 435	7
+ 759	7
+ 957	2
+ 295	9
+ 4	7
+ 403	9
+ 548	6
+ 943	4
+ 622	9
+ 305	6
+ 235	1
+ 124	1
+ 381	7
+ 789	1
+ 312	10
+ 434	7
+ 619	2
+ 398	6
+ 351	7
+ 489	4
+ 442	9
+ 279	10
+ 463	2
+ 418	1
+ 158	7
+ 720	4
+ 819	8
+ 473	2
+ 496	3
+ 349	8
+ 226	8
+ 556	8
+ 976	10
+ 421	3
+ 648	9
+ 683	1
+ 803	10
+ 80	3
+ 184	5
+ 352	3
+ 221	1
+ 736	0
+ 917	2
+ 240	4
+ 470	6
+ 221	7
+ 372	8
+ 542	3
+ 731	10
+ 676	4
+ 874	4
+ 469	7
+ 321	5
+ 943	5
+ 46	3
+ 848	3
+ 367	6
+ 307	3
+ 793	5
+ 697	3
+ 135	9
+ 959	5
+ 695	5
+ 855	4
+ 464	5
+ 806	3
+ 890	3
+ 14	2
+ 822	10
+ 715	9
+ 253	6
+ 135	6
+ 147	4
+ 904	9
+ 988	6
+ 203	1
+ 519	2
+ 630	2
+ 663	5
+ 640	1
+ 16	4
+ 465	9
+ 720	5
+ 115	5
+ 437	8
+ 410	7
+ 393	5
+ 309	5
+ 987	2
+ 479	10
+ 814	7
+ 97	3
+ 844	7
+ 547	5
+ 212	2
+ 634	2
+ 634	1
+ 133	4
+ 579	2
+ 896	0
+ 79	3
+ 706	5
+ 852	0
+ 12	8
+ 228	5
+ 813	0
+ 173	9
+ 376	0
+ 637	9
+ 524	8
+ 111	2
+ 76	7
+ 258	2
+ 98	8
+ 457	10
+ 853	5
+ 301	6
+ 8	2
+ 574	0
+ 991	8
+ 511	8
+ 845	7
+ 713	2
+ 702	4
+ 144	2
+ 199	3
+ 385	3
+ 999	6
+ 483	1
+ 481	9
+ 91	3
+ 475	4
+ 893	5
+ 544	5
+ 503	5
+ 270	0
+ 338	1
+ 698	1
+ 336	4
+ 402	5
+ 626	6
+ 735	0
+ 875	7
+ 655	4
+ 830	1
+ 298	9
+ 469	8
+ 313	4
+ 256	9
+ 830	8
+ 392	1
+ 773	7
+ 215	5
+ 782	6
+ 871	2
+ 31	5
+ 784	8
+ 509	7
+ 499	2
+ 17	3
+ 299	3
+ 250	8
+ 89	6
+ 130	3
+ 421	10
+ 104	8
+ 59	9
+ 543	3
+ 348	3
+ 824	2
+ 508	9
+ 717	3
+ 620	2
+ 950	1
+ 390	10
+ 448	7
+ 282	7
+ 457	4
+ 262	6
+ 716	7
+ 546	8
+ 496	6
+ 697	0
+ 879	0
+ 363	7
+ 265	9
+ 557	10
+ 163	2
+ 209	1
+ 296	6
+ 80	7
+ 288	4
+ 442	7
+ 733	7
+ 332	4
+ 387	9
+ 269	9
+ 483	10
+ 921	4
+ 12	3
+ 64	3
+ 155	6
+ 260	3
+ 799	5
+ 431	1
+ 68	5
+ 839	4
+ 873	3
+ 101	6
+ 986	4
+ 55	4
+ 311	3
+ 255	8
+ 290	2
+ 155	3
+ 460	2
+ 579	6
+ 840	8
+ 933	6
+ 308	4
+ 735	4
+ 875	6
+ 733	7
+ 855	8
+ 353	8
+ 268	4
+ 213	6
+ 732	5
+ 372	0
+ 644	5
+ 324	1
+ 746	9
+ 718	6
+ 743	7
+ 225	1
+ 15	10
+ 428	9
+ 534	2
+ 637	4
+ 996	10
+ 230	3
+ 399	4
+ 842	1
+ 911	2
+ 153	6
+ 741	5
+ 658	5
+ 380	4
+ 72	1
+ 28	3
+ 174	0
+ 258	6
+ 933	8
+ 763	6
+ 181	8
+ 561	4
+ 22	10
+ 854	9
+ 90	8
+ 78	2
+ 320	8
+ 719	10
+ 305	1
+ 354	4
+ 222	4
+ 675	4
+ 425	9
+ 997	4
+ 725	8
+ 928	9
+ 518	5
+ 317	5
+ 447	2
+ 405	5
+ 936	5
+ 780	3
+ 302	5
+ 233	6
+ 598	6
+ 985	8
+ 969	7
+ 215	4
+ 594	2
+ 752	3
+ 973	7
+ 224	5
+ 167	5
+ 32	6
+ 712	4
+ 152	6
+ 920	9
+ 903	2
+ 430	1
+ 830	0
+ 724	8
+ 848	7
+ 477	1
+ 88	1
+ 276	8
+ 389	2
+ 519	6
+ 740	7
+ 154	8
+ 301	9
+ 209	5
+ 514	1
+ 385	4
+ 351	8
+ 553	2
+ 843	3
+ 998	7
+ 971	5
+ 754	1
+ 545	0
+ 898	9
+ 279	4
+ 547	0
+ 104	7
+ 791	4
+ 568	10
+ 858	1
+ 129	2
+ 499	5
+ 58	1
+ 662	9
+ 330	7
+ 592	3
+ 134	3
+ 359	7
+ 376	3
+ 613	7
+ 675	2
+ 674	8
+ 862	5
+ 183	4
+ 465	0
+ 512	6
+ 284	0
+ 74	3
+ 63	7
+ 244	4
+ 395	8
+ 693	5
+ 181	1
+ 208	6
+ 309	8
+ 212	10
+ 981	9
+ 763	8
+ 352	9
+ 273	8
+ 988	8
+ 410	3
+ 796	5
+ 614	9
+ 220	9
+ 251	6
+ 693	9
+ 144	9
+ 995	4
+ 432	3
+ 174	6
+ 289	2
+ 531	1
+ 998	9
+ 997	3
+ 700	10
+ 57	1
+ 257	9
+ 594	9
+ 711	8
+ 730	10
+ 429	4
+ 905	6
+ 298	9
+ 926	7
+ 205	1
+ 374	5
+ 254	9
+ 545	3
+ 788	5
+ 524	5
+ 528	6
+ 598	8
+ 433	2
+ 656	1
+ 6	4
+ 105	4
+ 809	0
+ 8	1
+ 910	9
+ 836	1
+ 34	2
+ 608	3
+ 115	2
+ 541	9
+ 696	1
+ 391	2
+ 645	10
+ 8	1
+ 180	7
+ 220	2
+ 51	3
+ 621	9
+ 335	6
+ 966	2
+ 564	8
+ 359	6
+ 12	10
+ 887	1
+ 120	4
+ 31	8
+ 492	4
+ 40	1
+ 410	0
+ 213	6
+ 713	4
+ 777	8
+ 759	4
+ 623	1
+ 28	6
+ 338	6
+ 390	7
+ 191	4
+ 663	1
+ 530	8
+ 505	6
+ 599	10
+ 983	6
+ 133	4
+ 687	3
+ 984	4
+ 780	8
+ 163	5
+ 160	8
+ 632	2
+ 374	10
+ 780	8
+ 666	10
+ 167	3
+ 48	7
+ 112	6
+ 258	7
+ 549	2
+ 350	7
+ 635	0
+ 27	6
+ 437	8
+ 380	6
+ 345	5
+ 386	10
+ 727	8
+ 947	5
+ 525	6
+ 477	7
+ 942	5
+ 389	1
+ 77	6
+ 765	6
+ 889	1
+ 308	5
+ 153	3
+ 142	6
+ 143	5
+ 191	5
+ 62	6
+ 465	8
+ 338	4
+ 296	9
+ 25	8
+ 555	10
+ 298	9
+ 20	4
+ 591	8
+ 2	5
+ 901	3
+ 3	1
+ 645	1
+ 645	8
+ 667	8
+ 276	7
+ 413	7
+ 517	8
+ 153	8
+ 613	2
+ 586	2
+ 144	9
+ 112	2
+ 259	7
+ 949	3
+ 183	9
+ 570	2
+ 904	2
+ 331	5
+ 3	10
+ 385	3
+ 726	8
+ 20	2
+ 549	2
+ 56	2
+ 351	6
+ 330	5
+ 525	4
+ 658	8
+ 144	6
+ 45	3
+ 458	6
+ 513	4
+ 830	8
+ 911	8
+ 841	3
+ 112	6
+ 94	1
+ 810	6
+ 305	9
+ 806	7
+ 508	1
+ 150	0
+ 577	8
+ 817	7
+ 416	9
+ 49	9
+ 477	6
+ 236	3
+ 405	1
+ 140	2
+ 443	3
+ 812	5
+ 385	6
+ 181	7
+ 489	10
+ 345	10
+ 122	5
+ 30	7
+ 304	8
+ 421	7
+ 710	5
+ 594	2
+ 31	8
+ 493	4
+ 977	6
+ 681	4
+ 886	5
+ 958	3
+ 116	1
+ 960	6
+ 126	3
+ 602	2
+ 801	6
+ 948	1
+ 480	4
+ 825	2
+ 840	4
+ 376	9
+ 249	9
+ 307	2
+ 504	10
+ 646	4
+ 482	6
+ 661	6
+ 744	6
+ 203	9
+ 927	8
+ 118	7
+ 438	1
+ 833	9
+ 436	7
+ 108	3
+ 77	5
+ 147	3
+ 354	5
+ 552	9
+ 443	2
+ 248	9
+ 802	9
+ 523	5
+ 530	7
+ 416	5
+ 532	5
+ 186	10
+ 600	0
+ 887	0
+ 677	10
+ 312	8
+ 479	5
+ 80	8
+ 913	6
+ 691	4
+ 830	9
+ 281	6
+ 848	8
+ 178	4
+ 530	6
+ 835	1
+ 128	0
+ 31	7
+ 39	9
+ 765	7
+ 914	1
+ 470	4
+ 537	6
+ 226	4
+ 183	9
+ 806	0
+ 855	1
+ 645	7
+ 890	8
+ 81	4
+ 418	9
+ 482	5
+ 937	5
+ 274	10
+ 432	0
+ 692	3
+ 116	2
+ 738	7
+ 713	10
+ 102	9
+ 881	9
+ 909	7
+ 994	6
+ 439	9
+ 378	5
+ 304	8
+ 436	8
+ 341	4
+ 299	6
+ 349	7
+ 653	0
+ 76	8
+ 203	8
+ 421	9
+ 778	5
+ 812	7
+ 431	7
+ 395	4
+ 275	8
+ 309	7
+ 354	6
+ 449	8
+ 398	8
+ 163	7
+ 405	5
+ 428	1
+ 552	5
+ 828	8
+ 319	2
+ 672	1
+ 772	5
+ 756	2
+ 205	2
+ 628	5
+ 986	9
+ 134	3
+ 550	6
+ 130	9
+ 373	3
+ 644	8
+ 805	1
+ 837	4
+ 576	7
+ 113	9
+ 913	8
+ 992	7
+ 270	7
+ 889	5
+ 899	5
+ 956	9
+ 455	1
+ 225	0
+ 673	4
+ 952	0
+ 648	6
+ 825	5
+ 669	7
+ 811	2
+ 326	9
+ 140	2
+ 710	1
+ 925	10
+ 881	8
+ 454	8
+ 331	4
+ 665	8
+ 500	9
+ 791	2
+ 245	7
+ 219	9
+ 339	0
+ 347	0
+ 705	2
+ 253	0
+ 82	4
+ 270	8
+ 526	2
+ 771	4
+ 8	2
+ 186	3
+ 635	9
+ 126	1
+ 741	9
+ 307	10
+ 659	5
+ 878	10
+ 570	2
+ 5	3
+ 383	3
+ 306	5
+ 651	6
+ 256	2
+ 769	0
+ 583	8
+ 251	8
+ 117	9
+ 620	2
+ 21	4
+ 158	3
+ 346	8
+ 854	2
+ 814	4
+ 449	8
+ 699	8
+ 78	0
+ 296	7
+ 580	6
+ 905	3
+ 578	5
+ 127	8
+ 257	2
+ 715	9
+ 486	7
+ 237	6
+ 64	6
+ 461	9
+ 808	3
+ 342	3
+ 95	0
+ 89	2
+ 47	4
+ 901	6
+ 937	8
+ 977	5
+ 294	1
+ 344	6
+ 348	1
+ 428	8
+ 795	7
+ 478	9
+ 249	9
+ 777	1
+ 215	1
+ 314	3
+ 161	4
+ 482	2
+ 787	4
+ 835	7
+ 190	8
+ 238	5
+ 917	6
+ 36	3
+ 641	5
+ 100	4
+ 130	6
+ 295	4
+ 517	1
+ 436	7
+ 191	7
+ 42	4
+ 152	5
+ 559	9
+ 908	4
+ 663	1
+ 207	9
+ 583	1
+ 483	6
+ 390	1
+ 84	5
+ 561	2
+ 67	9
+ 593	6
+ 928	0
+ 316	1
+ 780	4
+ 470	9
+ 882	0
+ 871	8
+ 424	5
+ 888	6
+ 434	5
+ 756	9
+ 90	1
+ 42	2
+ 636	6
+ 387	7
+ 459	10
+ 288	4
+ 11	6
+ 505	8
+ 962	10
+ 722	8
+ 4	6
+ 634	4
+ 125	5
+ 59	6
+ 994	8
+ 476	1
+ 962	5
+ 257	6
+ 121	6
+ 301	6
+ 625	6
+ 966	6
+ 193	5
+ 426	2
+ 445	1
+ 1000	4
+ 739	6
+ 876	9
+ 157	9
+ 424	2
+ 751	9
+ 234	7
+ 418	5
+ 310	5
+ 135	6
+ 118	8
+ 200	1
+ 396	4
+ 555	8
+ 548	0
+ 969	5
+ 449	7
+ 183	3
+ 572	3
+ 261	10
+ 490	0
+ 896	7
+ 724	3
+ 214	0
+ 853	3
+ 645	10
+ 109	8
+ 56	5
+ 237	6
+ 326	8
+ 611	3
+ 334	1
+ 3	5
+ 385	6
+ 856	6
+ 571	3
+ 658	5
+ 69	4
+ 782	3
+ 415	6
+ 633	1
+ 607	7
+ 904	7
+ 248	1
+ 274	6
+ 927	9
+ 869	3
+ 945	9
+ 777	3
+ 447	6
+ 977	0
+ 978	6
+ 485	0
+ 16	3
+ 331	4
+ 902	10
+ 491	5
+ 707	4
+ 172	10
+ 537	4
+ 528	5
+ 331	4
+ 724	3
+ 268	5
+ 607	7
+ 134	6
+ 733	1
+ 219	2
+ 159	2
+ 485	5
+ 666	4
+ 455	2
+ 897	2
+ 552	1
+ 116	1
+ 515	6
+ 552	8
+ 42	3
+ 123	3
+ 777	7
+ 25	9
+ 314	8
+ 23	5
+ 975	2
+ 767	5
+ 673	4
+ 849	1
+ 591	7
+ 290	1
+ 815	4
+ 232	3
+ 51	8
+ 176	1
+ 61	3
+ 403	8
+ 28	4
+ 748	3
+ 185	8
+ 875	2
+ 953	6
+ 621	6
+ 76	5
+ 754	7
+ 216	0
+ 810	0
+ 451	0
+ 360	5
+ 826	5
+ 596	9
+ 834	10
+ 724	9
+ 426	5
+ 205	6
+ 244	1
+ 771	2
+ 724	4
+ 823	8
+ 863	6
+ 466	1
+ 622	3
+ 109	1
+ 318	5
+ 576	1
+ 6	2
+ 30	8
+ 170	8
+ 702	6
+ 226	9
+ 207	5
+ 989	10
+ 667	7
+ 372	5
+ 512	2
+ 67	10
+ 313	7
+ 254	4
+ 762	6
+ 892	3
+ 715	9
+ 510	7
+ 738	7
+ 498	4
+ 276	7
+ 348	5
+ 194	3
+ 462	9
+ 49	8
+ 350	6
+ 68	4
+ 539	4
+ 106	8
+ 804	9
+ 365	7
+ 207	1
+ 595	7
+ 824	3
+ 397	3
+ 773	7
+ 47	1
+ 156	2
+ 457	6
+ 101	5
+ 452	5
+ 66	5
+ 869	6
+ 902	10
+ 397	7
+ 844	8
+ 403	1
+ 841	10
+ 768	7
+ 330	2
+ 988	1
+ 837	0
+ 223	10
+ 276	7
+ 611	4
+ 185	1
+ 829	3
+ 583	7
+ 855	5
+ 672	3
+ 190	5
+ 14	6
+ 567	9
+ 590	3
+ 521	9
+ 498	5
+ 22	3
+ 544	2
+ 328	8
+ 925	9
+ 197	1
+ 1	0
+ 361	6
+ 723	2
+ 68	4
+ 469	3
+ 911	5
+ 851	5
+ 338	4
+ 812	9
+ 361	3
+ 368	4
+ 645	9
+ 629	10
+ 732	6
+ 911	9
+ 663	9
+ 955	0
+ 495	7
+ 241	6
+ 74	7
+ 820	10
+ 192	7
+ 462	5
+ 112	3
+ 388	5
+ 584	8
+ 856	2
+ 667	5
+ 201	4
+ 38	1
+ 329	7
+ 24	3
+ 726	5
+ 963	10
+ 81	0
+ 676	9
+ 21	9
+ 573	5
+ 398	7
+ 757	8
+ 157	3
+ 542	0
+ 569	2
+ 498	8
+ 608	5
+ 882	9
+ 238	9
+ 221	10
+ 424	2
+ 931	5
+ 221	6
+ 407	2
+ 476	10
+ 725	9
+ 664	5
+ 660	8
+ 822	2
+ 835	4
+ 411	3
+ 160	0
+ 870	0
+ 956	1
+ 947	2
+ 73	4
+ 362	0
+ 877	6
+ 612	3
+ 824	1
+ 265	5
+ 963	9
+ 31	6
+ 751	9
+ 825	6
+ 243	2
+ 920	4
+ 256	8
+ 445	2
+ 898	4
+ 390	10
+ 764	8
+ 975	6
+ 335	6
+ 926	2
+ 675	2
+ 708	6
+ 121	7
+ 261	9
+ 592	1
+ 458	8
+ 323	4
+ 238	6
+ 167	7
+ 791	1
+ 75	2
+ 36	8
+ 933	0
+ 481	3
+ 597	4
+ 427	3
+ 598	1
+ 910	7
+ 874	2
+ 590	5
+ 258	0
+ 300	6
+ 425	5
+ 160	6
+ 221	10
+ 657	3
+ 131	7
+ 134	1
+ 703	6
+ 332	3
+ 22	8
+ 573	6
+ 894	5
+ 339	8
+ 655	9
+ 234	9
+ 978	5
+ 494	4
+ 73	7
+ 995	3
+ 603	7
+ 588	7
+ 345	7
+ 799	0
+ 338	1
+ 349	4
+ 889	9
+ 980	8
+ 404	3
+ 551	1
+ 249	8
+ 972	2
+ 319	5
+ 629	4
+ 118	6
+ 685	7
+ 277	3
+ 456	6
+ 996	3
+ 670	3
+ 385	0
+ 694	3
+ 940	7
+ 57	3
+ 993	6
+ 404	2
+ 392	4
+ 468	7
+ 840	1
+ 103	10
+ 721	8
+ 680	10
+ 61	1
+ 620	1
+ 392	3
+ 391	8
+ 310	1
+ 51	3
+ 759	1
+ 595	8
+ 716	10
+ 993	1
+ 374	5
+ 819	2
+ 558	9
+ 172	3
+ 710	9
+ 278	8
+ 989	9
+ 829	4
+ 188	2
+ 158	5
+ 305	2
+ 748	1
+ 317	3
+ 815	0
+ 341	8
+ 141	7
+ 270	10
+ 929	8
+ 883	1
+ 108	6
+ 954	4
+ 364	9
+ 283	2
+ 324	5
+ 413	5
+ 970	7
+ 691	7
+ 781	0
+ 61	6
+ 41	4
+ 405	2
+ 118	7
+ 142	0
+ 504	0
+ 148	6
+ 618	1
+ 997	10
+ 46	3
+ 175	4
+ 752	6
+ 852	7
+ 306	5
+ 440	1
+ 552	5
+ 684	6
+ 904	1
+ 775	0
+ 764	9
+ 68	3
+ 942	2
+ 880	6
+ 319	9
+ 542	4
+ 157	7
+ 734	9
+ 306	6
+ 632	6
+ 130	1
+ 699	7
+ 574	4
+ 275	5
+ 472	1
+ 500	2
+ 968	6
+ 505	9
+ 785	4
+ 470	1
+ 261	0
+ 468	4
+ 730	2
+ 328	0
+ 788	10
+ 648	9
+ 32	3
+ 601	6
+ 730	9
+ 83	2
+ 926	6
+ 439	9
+ 151	9
+ 803	9
+ 327	3
+ 39	6
+ 285	5
+ 7	0
+ 709	3
+ 52	5
+ 294	7
+ 416	3
+ 47	0
+ 931	8
+ 892	0
+ 979	8
+ 597	4
+ 712	7
+ 361	5
+ 685	7
+ 789	7
+ 277	1
+ 231	3
+ 89	9
+ 618	1
+ 437	9
+ 840	9
+ 237	9
+ 869	2
+ 664	8
+ 181	6
+ 580	8
+ 61	3
+ 527	4
+ 808	2
+ 110	6
+ 936	4
+ 671	2
+ 670	8
+ 106	3
+ 901	5
+ 199	7
+ 395	4
+ 629	3
+ 603	3
+ 26	8
+ 936	6
+ 562	10
+ 898	1
+ 418	7
+ 301	5
+ 303	2
+ 915	10
+ 403	6
+ 733	5
+ 873	6
+ 52	1
+ 376	4
+ 508	0
+ 712	1
+ 297	7
+ 894	2
+ 344	5
+ 229	2
+ 546	6
+ 948	8
+ 176	3
+ 84	1
+ 225	5
+ 677	10
+ 996	5
+ 592	0
+ 622	10
+ 495	1
+ 972	2
+ 240	3
+ 944	1
+ 502	3
+ 591	7
+ 530	1
+ 379	5
+ 984	6
+ 730	1
+ 646	10
+ 555	3
+ 912	6
+ 873	5
+ 598	5
+ 472	1
+ 625	4
+ 299	9
+ 713	2
+ 1000	2
+ 531	6
+ 946	1
+ 728	3
+ 540	7
+ 881	3
+ 781	5
+ 223	3
+ 850	1
+ 885	7
+ 639	5
+ 218	1
+ 576	8
+ 555	9
+ 707	3
+ 120	7
+ 483	7
+ 298	4
+ 712	0
+ 755	3
+ 739	6
+ 521	5
+ 162	7
+ 854	0
+ 880	7
+ 735	5
+ 223	10
+ 630	8
+ 795	2
+ 676	5
+ 454	8
+ 208	9
+ 446	5
+ 367	2
+ 532	1
+ 410	3
+ 757	9
+ 789	9
+ 676	6
+ 931	6
+ 384	7
+ 74	6
+ 618	7
+ 407	4
+ 890	1
+ 915	3
+ 878	1
+ 281	3
+ 629	6
+ 482	2
+ 769	9
+ 431	5
+ 824	2
+ 445	5
+ 865	4
+ 55	2
+ 42	1
+ 855	7
+ 834	3
+ 73	7
+ 344	10
+ 68	2
+ 111	3
+ 545	7
+ 996	0
+ 901	8
+ 920	3
+ 291	7
+ 553	7
+ 243	4
+ 111	3
+ 666	2
+ 427	5
+ 812	3
+ 783	9
+ 985	1
+ 873	1
+ 348	10
+ 401	9
+ 724	4
+ 921	6
+ 163	8
+ 957	5
+ 584	5
+ 189	8
+ 928	3
+ 125	6
+ 452	6
+ 114	3
+ 814	9
+ 150	8
+ 23	0
+ 851	4
+ 7	3
+ 265	7
+ 650	2
+ 356	8
+ 25	3
+ 266	6
+ 824	5
+ 436	8
+ 754	6
+ 345	2
+ 114	5
+ 471	9
+ 356	6
+ 726	4
+ 644	6
+ 751	7
+ 829	0
+ 383	5
+ 201	7
+ 291	2
+ 53	6
+ 836	9
+ 11	3
+ 628	8
+ 834	10
+ 972	9
+ 433	4
+ 875	8
+ 63	6
+ 170	7
+ 178	9
+ 359	0
+ 937	7
+ 487	1
+ 481	8
+ 365	5
+ 335	2
+ 411	3
+ 472	0
+ 112	3
+ 13	1
+ 254	4
+ 526	1
+ 236	6
+ 730	4
+ 297	9
+ 327	7
+ 916	3
+ 399	4
+ 402	9
+ 181	8
+ 414	5
+ 967	8
+ 862	4
+ 865	10
+ 745	9
+ 58	10
+ 325	6
+ 128	6
+ 173	9
+ 967	5
+ 767	3
+ 127	7
+ 558	5
+ 86	10
+ 405	3
+ 726	8
+ 783	7
+ 645	6
+ 132	5
+ 619	9
+ 388	7
+ 876	7
+ 260	0
+ 273	4
+ 862	2
+ 903	6
+ 534	0
+ 312	1
+ 555	4
+ 51	10
+ 665	8
+ 780	4
+ 469	4
+ 93	6
+ 934	7
+ 477	3
+ 388	4
+ 34	6
+ 356	3
+ 81	2
+ 546	10
+ 847	1
+ 15	2
+ 171	6
+ 558	2
+ 531	2
+ 998	3
+ 672	5
+ 735	8
+ 67	7
+ 476	5
+ 991	9
+ 897	0
+ 512	3
+ 332	6
+ 471	9
+ 578	3
+ 958	6
+ 477	1
+ 163	0
+ 351	7
+ 259	3
+ 4	9
+ 816	7
+ 695	9
+ 409	2
+ 427	4
+ 35	3
+ 426	5
+ 576	8
+ 140	0
+ 636	7
+ 365	6
+ 311	8
+ 724	5
+ 877	1
+ 167	1
+ 424	2
+ 67	2
+ 911	8
+ 122	3
+ 932	5
+ 721	10
+ 872	1
+ 514	4
+ 905	7
+ 495	5
+ 372	9
+ 135	7
+ 702	9
+ 156	6
+ 933	3
+ 715	4
+ 495	8
+ 596	4
+ 543	7
+ 726	5
+ 267	4
+ 442	1
+ 595	10
+ 588	5
+ 610	1
+ 40	10
+ 943	2
+ 665	6
+ 34	8
+ 224	10
+ 145	9
+ 324	6
+ 721	9
+ 45	3
+ 638	8
+ 739	9
+ 219	2
+ 45	8
+ 138	6
+ 314	7
+ 717	4
+ 730	7
+ 529	4
+ 305	6
+ 216	5
+ 531	4
+ 468	9
+ 0	2
+ 776	0
+ 453	4
+ 817	2
+ 320	0
+ 373	4
+ 850	5
+ 998	2
+ 259	7
+ 518	10
+ 375	0
+ 384	7
+ 611	6
+ 209	1
+ 961	7
+ 998	10
+ 866	8
+ 7	3
+ 188	8
+ 511	5
+ 861	9
+ 873	7
+ 395	9
+ 875	7
+ 586	4
+ 643	10
+ 441	0
+ 642	1
+ 628	9
+ 195	6
+ 529	2
+ 551	4
+ 967	6
+ 714	2
+ 383	2
+ 663	2
+ 109	5
+ 955	5
+ 408	8
+ 158	10
+ 223	8
+ 956	7
+ 829	6
+ 717	5
+ 449	9
+ 46	10
+ 104	6
+ 373	1
+ 156	1
+ 226	5
+ 313	9
+ 781	4
+ 426	7
+ 926	8
+ 566	1
+ 830	8
+ 886	8
+ 454	7
+ 384	2
+ 172	8
+ 82	2
+ 811	2
+ 816	2
+ 257	10
+ 272	5
+ 509	6
+ 373	3
+ 6	8
+ 27	9
+ 635	6
+ 16	5
+ 382	9
+ 250	8
+ 617	6
+ 7	8
+ 468	1
+ 7	3
+ 275	8
+ 464	5
+ 794	7
+ 16	3
+ 320	4
+ 593	3
+ 189	6
+ 259	8
+ 213	3
+ 288	6
+ 177	5
+ 431	8
+ 173	4
+ 583	6
+ 527	6
+ 920	8
+ 413	4
+ 335	2
+ 120	4
+ 509	4
+ 740	1
+ 767	9
+ 722	0
+ 754	9
+ 301	0
+ 530	5
+ 581	10
+ 273	8
+ 400	9
+ 395	9
+ 446	3
+ 728	9
+ 700	1
+ 65	8
+ 414	6
+ 260	2
+ 676	0
+ 84	4
+ 52	8
+ 334	4
+ 880	9
+ 832	5
+ 826	1
+ 216	2
+ 960	6
+ 152	4
+ 927	9
+ 265	6
+ 943	3
+ 446	4
+ 904	7
+ 511	6
+ 733	6
+ 979	8
+ 433	3
+ 138	3
+ 177	10
+ 775	0
+ 74	10
+ 227	0
+ 603	4
+ 441	5
+ 259	7
+ 157	2
+ 37	6
+ 559	9
+ 309	1
+ 522	0
+ 666	5
+ 827	1
+ 814	10
+ 413	10
+ 935	2
+ 993	0
+ 180	2
+ 44	8
+ 599	5
+ 312	9
+ 191	5
+ 61	2
+ 73	6
+ 169	4
+ 691	7
+ 424	4
+ 192	3
+ 456	0
+ 217	9
+ 997	2
+ 57	10
+ 162	2
+ 210	2
+ 19	8
+ 690	3
+ 668	9
+ 801	7
+ 109	9
+ 350	3
+ 256	0
+ 969	7
+ 399	2
+ 932	9
+ 168	1
+ 724	2
+ 301	8
+ 154	5
+ 19	4
+ 668	0
+ 173	4
+ 370	8
+ 239	2
+ 570	3
+ 45	9
+ 626	3
+ 962	6
+ 982	4
+ 757	9
+ 216	9
+ 63	9
+ 89	4
+ 722	2
+ 828	7
+ 606	5
+ 779	8
+ 854	1
+ 619	1
+ 320	2
+ 441	4
+ 110	1
+ 666	1
+ 663	6
+ 432	4
+ 562	6
+ 344	6
+ 588	4
+ 989	3
+ 676	8
+ 51	3
+ 313	8
+ 61	2
+ 978	7
+ 260	3
+ 870	7
+ 663	10
+ 768	3
+ 50	4
+ 978	5
+ 850	5
+ 129	2
+ 165	7
+ 628	2
+ 27	3
+ 971	1
+ 586	3
+ 907	6
+ 450	9
+ 327	7
+ 184	2
+ 410	8
+ 175	2
+ 177	2
+ 608	2
+ 707	5
+ 694	8
+ 652	9
+ 554	3
+ 13	6
+ 584	10
+ 658	2
+ 267	6
+ 816	7
+ 450	1
+ 428	6
+ 339	8
+ 480	5
+ 16	7
+ 739	6
+ 811	4
+ 82	5
+ 283	7
+ 364	8
+ 15	4
+ 417	6
+ 360	1
+ 769	6
+ 640	6
+ 345	1
+ 728	8
+ 723	1
+ 611	2
+ 581	6
+ 861	3
+ 252	7
+ 767	3
+ 177	1
+ 69	5
+ 887	1
+ 918	3
+ 684	3
+ 380	5
+ 906	0
+ 38	3
+ 110	8
+ 24	8
+ 833	6
+ 37	4
+ 263	9
+ 733	5
+ 570	5
+ 849	7
+ 550	9
+ 288	4
+ 2	2
+ 742	7
+ 484	1
+ 139	4
+ 142	2
+ 640	3
+ 942	7
+ 85	8
+ 300	1
+ 188	6
+ 20	9
+ 76	6
+ 422	9
+ 336	10
+ 843	6
+ 409	8
+ 830	2
+ 531	3
+ 274	7
+ 704	4
+ 846	3
+ 667	8
+ 8	8
+ 564	3
+ 874	8
+ 870	9
+ 674	9
+ 483	1
+ 871	8
+ 68	7
+ 444	5
+ 559	3
+ 629	1
+ 588	9
+ 760	3
+ 318	6
+ 636	10
+ 395	6
+ 737	10
+ 951	6
+ 711	8
+ 505	4
+ 767	10
+ 480	6
+ 807	5
+ 353	3
+ 25	9
+ 525	7
+ 2	1
+ 555	8
+ 405	9
+ 369	0
+ 858	8
+ 684	6
+ 723	6
+ 207	4
+ 456	7
+ 818	2
+ 701	3
+ 862	5
+ 845	2
+ 759	9
+ 127	3
+ 523	1
+ 397	1
+ 892	8
+ 952	3
+ 841	8
+ 25	5
+ 406	7
+ 160	6
+ 181	6
+ 325	10
+ 840	0
+ 297	7
+ 534	1
+ 916	3
+ 13	0
+ 577	5
+ 172	10
+ 615	1
+ 775	6
+ 325	6
+ 377	3
+ 141	8
+ 97	3
+ 396	3
+ 918	7
+ 278	8
+ 747	6
+ 459	3
+ 717	4
+ 574	7
+ 418	2
+ 265	6
+ 125	9
+ 655	9
+ 447	10
+ 516	8
+ 329	7
+ 606	4
+ 959	0
+ 705	9
+ 723	10
+ 634	5
+ 557	1
+ 751	3
+ 468	3
+ 4	9
+ 477	3
+ 477	6
+ 149	1
+ 501	6
+ 111	0
+ 419	4
+ 674	0
+ 867	6
+ 28	6
+ 509	8
+ 554	1
+ 221	1
+ 236	10
+ 385	7
+ 298	4
+ 590	8
+ 657	1
+ 375	8
+ 200	9
+ 402	3
+ 893	8
+ 752	6
+ 847	6
+ 199	9
+ 190	7
+ 626	7
+ 852	8
+ 854	1
+ 819	2
+ 792	1
+ 627	4
+ 891	3
+ 450	3
+ 91	6
+ 142	5
+ 961	0
+ 315	7
+ 602	2
+ 331	8
+ 37	5
+ 510	7
+ 265	4
+ 509	1
+ 450	3
+ 358	2
+ 445	10
+ 624	3
+ 270	1
+ 602	4
+ 724	7
+ 854	7
+ 779	2
+ 397	4
+ 331	7
+ 182	4
+ 248	7
+ 31	5
+ 55	5
+ 632	5
+ 869	10
+ 746	3
+ 976	4
+ 649	2
+ 445	3
+ 606	2
+ 995	5
+ 853	8
+ 629	2
+ 155	10
+ 977	3
+ 329	2
+ 30	4
+ 738	1
+ 901	4
+ 589	8
+ 361	3
+ 84	3
+ 706	7
+ 582	2
+ 984	2
+ 319	10
+ 648	2
+ 753	3
+ 421	9
+ 237	4
+ 246	6
+ 623	3
+ 926	4
+ 361	8
+ 732	9
+ 597	1
+ 285	7
+ 430	10
+ 414	0
+ 141	4
+ 201	5
+ 378	8
+ 631	1
+ 126	1
+ 40	4
+ 449	3
+ 929	1
+ 562	9
+ 433	9
+ 682	2
+ 872	3
+ 258	2
+ 960	7
+ 147	4
+ 700	3
+ 773	9
+ 747	2
+ 749	4
+ 282	9
+ 429	3
+ 239	9
+ 608	2
+ 949	2
+ 23	4
+ 92	7
+ 546	10
+ 984	8
+ 121	9
+ 491	3
+ 318	2
+ 556	1
+ 91	3
+ 242	8
+ 680	5
+ 716	1
+ 846	10
+ 986	5
+ 123	9
+ 624	1
+ 317	7
+ 851	9
+ 681	8
+ 668	8
+ 778	2
+ 71	1
+ 350	6
+ 186	4
+ 929	4
+ 282	6
+ 952	10
+ 718	8
+ 952	7
+ 252	1
+ 639	9
+ 221	10
+ 593	1
+ 820	3
+ 906	5
+ 76	7
+ 647	1
+ 779	10
+ 774	10
+ 438	7
+ 393	7
+ 312	3
+ 718	0
+ 143	7
+ 734	4
+ 745	4
+ 271	10
+ 330	9
+ 37	1
+ 138	9
+ 637	2
+ 627	3
+ 361	4
+ 281	1
+ 372	7
+ 838	8
+ 440	1
+ 110	2
+ 180	3
+ 828	9
+ 647	6
+ 287	9
+ 538	6
+ 782	6
+ 766	9
+ 518	4
+ 133	1
+ 688	5
+ 551	10
+ 629	9
+ 689	5
+ 688	1
+ 616	8
+ 287	8
+ 50	1
+ 709	7
+ 687	10
+ 616	2
+ 613	4
+ 801	4
+ 316	3
+ 782	4
+ 464	5
+ 944	0
+ 439	6
+ 939	1
+ 39	6
+ 257	7
+ 425	5
+ 451	5
+ 658	2
+ 173	3
+ 157	8
+ 570	8
+ 186	4
+ 148	5
+ 690	9
+ 951	2
+ 400	9
+ 170	8
+ 468	1
+ 967	5
+ 735	2
+ 162	2
+ 768	6
+ 635	4
+ 774	8
+ 771	9
+ 596	3
+ 700	8
+ 712	8
+ 283	4
+ 778	2
+ 556	2
+ 129	7
+ 17	6
+ 834	10
+ 104	6
+ 208	3
+ 729	10
+ 879	4
+ 403	7
+ 171	2
+ 583	8
+ 516	3
+ 548	2
+ 131	8
+ 631	9
+ 66	2
+ 86	2
+ 912	1
+ 792	7
+ 86	9
+ 315	3
+ 161	0
+ 272	0
+ 408	7
+ 693	6
+ 850	3
+ 346	4
+ 559	9
+ 595	7
+ 726	2
+ 598	8
+ 412	7
+ 987	3
+ 786	8
+ 70	9
+ 676	4
+ 167	8
+ 430	4
+ 877	8
+ 113	6
+ 417	10
+ 846	8
+ 330	4
+ 657	9
+ 94	4
+ 150	7
+ 175	6
+ 376	2
+ 886	2
+ 942	10
+ 34	6
+ 342	2
+ 455	8
+ 639	3
+ 610	8
+ 902	0
+ 715	7
+ 789	0
+ 152	4
+ 969	2
+ 829	1
+ 938	0
+ 682	3
+ 166	6
+ 475	1
+ 525	5
+ 725	9
+ 709	2
+ 639	3
+ 511	2
+ 99	4
+ 275	8
+ 160	1
+ 859	3
+ 509	8
+ 558	3
+ 948	5
+ 341	6
+ 809	5
+ 198	3
+ 614	7
+ 792	3
+ 590	5
+ 519	2
+ 848	0
+ 478	9
+ 443	8
+ 761	6
+ 816	6
+ 916	3
+ 448	5
+ 663	4
+ 970	0
+ 26	8
+ 512	2
+ 62	1
+ 947	9
+ 465	5
+ 354	10
+ 766	2
+ 14	2
+ 149	5
+ 996	9
+ 61	8
+ 530	10
+ 138	10
+ 451	8
+ 374	4
+ 806	4
+ 199	3
+ 623	3
+ 443	6
+ 115	9
+ 107	5
+ 893	9
+ 671	9
+ 117	8
+ 365	1
+ 730	4
+ 926	3
+ 403	1
+ 237	9
+ 865	6
+ 275	7
+ 10	5
+ 988	6
+ 736	4
+ 204	9
+ 340	3
+ 321	2
+ 185	10
+ 140	3
+ 812	5
+ 416	5
+ 931	3
+ 802	3
+ 405	0
+ 189	3
+ 650	5
+ 941	7
+ 939	9
+ 295	7
+ 361	5
+ 526	7
+ 810	8
+ 934	10
+ 839	1
+ 297	7
+ 579	7
+ 194	5
+ 54	10
+ 845	5
+ 36	0
+ 730	7
+ 498	7
+ 346	4
+ 602	6
+ 112	10
+ 140	6
+ 665	9
+ 486	6
+ 945	3
+ 673	2
+ 977	3
+ 954	2
+ 763	0
+ 167	6
+ 468	2
+ 641	2
+ 888	1
+ 870	2
+ 576	5
+ 876	7
+ 434	0
+ 326	1
+ 965	8
+ 698	9
+ 136	4
+ 151	1
+ 624	1
+ 284	4
+ 114	5
+ 994	6
+ 654	6
+ 780	5
+ 773	7
+ 777	3
+ 122	7
+ 36	6
+ 669	4
+ 655	6
+ 174	4
+ 544	3
+ 724	7
+ 423	3
+ 801	7
+ 734	9
+ 158	7
+ 497	8
+ 362	3
+ 354	1
+ 928	1
+ 484	0
+ 784	5
+ 605	5
+ 882	3
+ 87	1
+ 613	6
+ 365	3
+ 326	8
+ 685	1
+ 495	4
+ 42	7
+ 148	5
+ 465	5
+ 816	8
+ 646	7
+ 950	1
+ 793	7
+ 649	4
+ 187	5
+ 658	3
+ 587	3
+ 904	10
+ 608	2
+ 740	3
+ 356	2
+ 712	4
+ 888	9
+ 937	4
+ 370	8
+ 172	0
+ 497	1
+ 146	3
+ 855	8
+ 687	0
+ 327	3
+ 315	9
+ 616	2
+ 866	2
+ 449	6
+ 516	8
+ 842	2
+ 203	7
+ 89	1
+ 83	5
+ 893	3
+ 475	4
+ 376	6
+ 679	2
+ 416	4
+ 272	7
+ 711	6
+ 656	3
+ 806	5
+ 550	3
+ 129	1
+ 60	10
+ 295	3
+ 701	4
+ 403	8
+ 843	3
+ 39	3
+ 686	4
+ 940	4
+ 645	4
+ 732	9
+ 99	4
+ 504	8
+ 770	3
+ 278	3
+ 565	4
+ 386	6
+ 377	7
+ 887	1
+ 65	3
+ 861	9
+ 586	9
+ 227	3
+ 315	2
+ 638	10
+ 523	4
+ 878	6
+ 812	4
+ 378	6
+ 693	7
+ 901	3
+ 62	3
+ 881	4
+ 968	8
+ 517	0
+ 58	4
+ 941	6
+ 278	2
+ 917	6
+ 335	6
+ 553	9
+ 923	4
+ 480	7
+ 813	9
+ 316	5
+ 514	2
+ 763	6
+ 504	6
+ 16	5
+ 413	5
+ 505	5
+ 911	4
+ 116	2
+ 614	0
+ 782	9
+ 587	3
+ 806	5
+ 767	3
+ 246	6
+ 145	6
+ 86	7
+ 780	8
+ 236	3
+ 494	3
+ 756	9
+ 785	3
+ 378	7
+ 707	5
+ 885	3
+ 527	7
+ 269	1
+ 4	1
+ 625	8
+ 362	9
+ 351	5
+ 433	4
+ 166	2
+ 287	4
+ 497	8
+ 655	3
+ 688	4
+ 514	1
+ 137	2
+ 561	0
+ 541	1
+ 690	8
+ 202	7
+ 885	8
+ 464	2
+ 698	8
+ 753	1
+ 252	9
+ 345	5
+ 322	8
+ 321	10
+ 95	0
+ 418	6
+ 76	6
+ 829	6
+ 576	4
+ 725	3
+ 180	9
+ 959	1
+ 755	4
+ 311	5
+ 238	1
+ 585	5
+ 983	9
+ 30	3
+ 772	4
+ 283	9
+ 360	7
+ 476	4
+ 256	3
+ 73	8
+ 675	8
+ 98	9
+ 726	1
+ 919	5
+ 480	2
+ 934	7
+ 293	5
+ 208	3
+ 450	2
+ 582	2
+ 588	9
+ 88	9
+ 567	6
+ 384	8
+ 869	5
+ 655	5
+ 255	8
+ 398	10
+ 810	3
+ 461	3
+ 546	4
+ 8	8
+ 915	2
+ 115	4
+ 453	7
+ 586	0
+ 562	7
+ 988	1
+ 238	4
+ 952	1
+ 829	6
+ 650	1
+ 359	0
+ 64	2
+ 365	5
+ 459	9
+ 920	5
+ 749	8
+ 683	9
+ 200	1
+ 561	8
+ 176	1
+ 460	2
+ 252	7
+ 537	2
+ 805	4
+ 810	5
+ 449	2
+ 503	5
+ 338	9
+ 37	8
+ 778	10
+ 264	5
+ 793	9
+ 390	10
+ 83	10
+ 778	3
+ 74	2
+ 424	3
+ 936	10
+ 530	7
+ 326	3
+ 196	8
+ 509	7
+ 287	8
+ 567	3
+ 645	3
+ 282	9
+ 871	1
+ 856	3
+ 68	9
+ 212	8
+ 198	3
+ 84	6
+ 613	0
+ 583	1
+ 761	9
+ 484	10
+ 684	10
+ 657	10
+ 841	2
+ 296	5
+ 569	6
+ 395	4
+ 653	3
+ 702	7
+ 190	9
+ 567	4
+ 201	7
+ 11	8
+ 670	6
+ 957	4
+ 504	4
+ 389	2
+ 435	0
+ 160	3
+ 270	5
+ 761	8
+ 34	2
+ 279	7
+ 407	10
+ 408	6
+ 894	10
+ 986	1
+ 625	10
+ 908	3
+ 592	9
+ 727	1
+ 307	1
+ 285	7
+ 162	4
+ 17	4
+ 900	8
+ 270	9
+ 934	5
+ 622	3
+ 529	0
+ 939	4
+ 5	9
+ 518	6
+ 923	4
+ 925	5
+ 292	7
+ 612	6
+ 768	9
+ 341	9
+ 341	4
+ 362	2
+ 136	6
+ 175	1
+ 181	8
+ 411	7
+ 826	4
+ 134	8
+ 275	7
+ 461	2
+ 79	4
+ 714	4
+ 38	3
+ 970	8
+ 222	3
+ 737	6
+ 668	1
+ 803	8
+ 732	10
+ 873	9
+ 774	3
+ 623	6
+ 635	8
+ 431	9
+ 409	9
+ 108	5
+ 278	8
+ 858	3
+ 147	8
+ 123	4
+ 139	9
+ 931	8
+ 959	7
+ 611	7
+ 712	5
+ 604	5
+ 769	2
+ 86	4
+ 985	5
+ 313	4
+ 408	4
+ 881	7
+ 243	7
+ 2	4
+ 568	1
+ 760	7
+ 985	7
+ 514	9
+ 426	1
+ 636	1
+ 608	2
+ 624	4
+ 467	7
+ 780	5
+ 227	1
+ 846	6
+ 514	7
+ 321	8
+ 467	3
+ 148	0
+ 448	9
+ 742	4
+ 598	3
+ 378	0
+ 380	0
+ 162	10
+ 253	8
+ 365	7
+ 495	1
+ 173	7
+ 238	0
+ 356	8
+ 746	7
+ 510	2
+ 0	7
+ 248	4
+ 565	10
+ 882	2
+ 246	3
+ 187	6
+ 272	3
+ 614	5
+ 134	10
+ 246	6
+ 125	4
+ 350	4
+ 437	7
+ 115	2
+ 384	6
+ 396	4
+ 282	6
+ 833	8
+ 634	7
+ 10	9
+ 973	2
+ 507	2
+ 545	1
+ 771	7
+ 100	0
+ 307	2
+ 435	7
+ 588	9
+ 364	7
+ 55	7
+ 327	5
+ 132	6
+ 95	10
+ 455	7
+ 679	5
+ 609	7
+ 661	1
+ 897	2
+ 237	7
+ 885	3
+ 685	2
+ 563	1
+ 849	2
+ 991	2
+ 853	0
+ 961	2
+ 497	1
+ 788	6
+ 57	2
+ 320	7
+ 708	9
+ 387	4
+ 46	3
+ 575	3
+ 953	5
+ 620	6
+ 652	2
+ 758	5
+ 333	7
+ 714	2
+ 795	7
+ 365	3
+ 767	2
+ 883	8
+ 396	2
+ 559	1
+ 133	9
+ 472	2
+ 232	0
+ 461	2
+ 507	1
+ 823	2
+ 264	6
+ 659	6
+ 329	4
+ 783	1
+ 47	1
+ 416	8
+ 300	3
+ 638	7
+ 502	2
+ 800	6
+ 145	3
+ 814	4
+ 319	3
+ 561	8
+ 356	4
+ 984	6
+ 964	6
+ 218	3
+ 16	0
+ 418	1
+ 148	8
+ 878	4
+ 133	5
+ 144	6
+ 714	9
+ 270	9
+ 217	1
+ 235	5
+ 358	8
+ 362	7
+ 180	3
+ 335	1
+ 989	6
+ 437	0
+ 553	9
+ 69	7
+ 689	9
+ 149	8
+ 463	3
+ 457	2
+ 238	7
+ 36	5
+ 810	3
+ 990	2
+ 67	4
+ 883	2
+ 698	2
+ 390	7
+ 771	8
+ 693	3
+ 683	8
+ 26	4
+ 709	2
+ 194	2
+ 469	7
+ 349	7
+ 377	4
+ 161	2
+ 656	2
+ 355	7
+ 503	2
+ 969	2
+ 456	4
+ 889	2
+ 187	6
+ 553	9
+ 344	6
+ 241	1
+ 754	4
+ 225	2
+ 85	6
+ 929	5
+ 960	1
+ 650	6
+ 241	0
+ 339	7
+ 244	3
+ 946	7
+ 668	8
+ 928	9
+ 418	5
+ 725	8
+ 59	10
+ 815	8
+ 400	0
+ 35	5
+ 614	10
+ 948	6
+ 53	6
+ 190	3
+ 604	5
+ 39	8
+ 838	10
+ 547	5
+ 820	5
+ 361	2
+ 955	1
+ 0	0
+ 52	8
+ 826	5
+ 855	9
+ 938	5
+ 825	9
+ 43	9
+ 484	2
+ 173	1
+ 762	2
+ 935	6
+ 196	5
+ 107	0
+ 957	5
+ 254	9
+ 554	3
+ 926	6
+ 69	8
+ 58	9
+ 614	10
+ 393	4
+ 882	4
+ 317	4
+ 669	5
+ 454	4
+ 701	4
+ 32	9
+ 871	1
+ 913	8
+ 607	2
+ 740	2
+ 421	7
+ 767	5
+ 418	8
+ 414	0
+ 821	8
+ 470	7
+ 244	8
+ 69	9
+ 277	5
+ 345	10
+ 912	4
+ 875	8
+ 516	8
+ 611	1
+ 956	4
+ 285	4
+ 16	1
+ 867	4
+ 878	3
+ 466	7
+ 88	9
+ 401	3
+ 723	5
+ 245	0
+ 992	6
+ 978	9
+ 967	9
+ 687	5
+ 642	3
+ 607	6
+ 648	9
+ 974	7
+ 944	8
+ 98	8
+ 122	6
+ 520	2
+ 500	9
+ 541	2
+ 391	8
+ 223	4
+ 376	2
+ 288	3
+ 55	10
+ 827	7
+ 273	4
+ 294	9
+ 325	3
+ 585	3
+ 108	7
+ 92	2
+ 248	6
+ 440	7
+ 533	10
+ 971	9
+ 767	2
+ 308	1
+ 395	6
+ 487	4
+ 571	3
+ 146	8
+ 747	4
+ 765	1
+ 707	4
+ 342	8
+ 34	4
+ 46	3
+ 46	5
+ 30	6
+ 466	0
+ 504	2
+ 194	8
+ 378	6
+ 408	9
+ 38	10
+ 178	2
+ 823	9
+ 624	6
+ 997	3
+ 939	3
+ 147	10
+ 772	2
+ 255	8
+ 677	3
+ 397	1
+ 286	9
+ 378	5
+ 712	8
+ 69	1
+ 620	1
+ 98	8
+ 291	9
+ 722	9
+ 509	7
+ 245	4
+ 58	4
+ 421	8
+ 584	7
+ 648	3
+ 962	0
+ 405	2
+ 945	8
+ 727	7
+ 538	8
+ 776	2
+ 903	9
+ 956	2
+ 796	7
+ 108	3
+ 397	4
+ 753	5
+ 745	2
+ 285	3
+ 850	9
+ 591	8
+ 977	10
+ 59	9
+ 780	8
+ 578	3
+ 583	4
+ 476	5
+ 228	4
+ 679	0
+ 110	8
+ 329	5
+ 141	1
+ 963	9
+ 255	2
+ 215	1
+ 181	8
+ 917	2
+ 803	10
+ 80	6
+ 763	7
+ 900	3
+ 12	4
+ 831	2
+ 809	5
+ 264	9
+ 297	6
+ 427	4
+ 674	4
+ 324	9
+ 638	5
+ 34	8
+ 346	10
+ 978	1
+ 928	1
+ 730	7
+ 716	6
+ 36	7
+ 7	9
+ 969	8
+ 378	2
+ 735	7
+ 826	2
+ 113	5
+ 552	4
+ 429	2
+ 976	5
+ 10	3
+ 415	10
+ 470	3
+ 46	2
+ 33	8
+ 831	1
+ 490	8
+ 937	5
+ 654	3
+ 691	4
+ 990	5
+ 550	1
+ 17	1
+ 539	4
+ 292	5
+ 909	3
+ 837	3
+ 289	3
+ 666	3
+ 507	7
+ 96	3
+ 769	6
+ 176	7
+ 45	8
+ 21	7
+ 218	0
+ 253	8
+ 113	3
+ 870	7
+ 715	2
+ 167	6
+ 463	0
+ 947	8
+ 312	6
+ 87	8
+ 312	2
+ 157	1
+ 770	3
+ 787	8
+ 163	8
+ 551	4
+ 818	8
+ 150	9
+ 74	0
+ 583	8
+ 182	8
+ 414	6
+ 755	4
+ 397	1
+ 974	5
+ 886	3
+ 668	0
+ 368	4
+ 377	2
+ 253	5
+ 964	8
+ 921	8
+ 609	1
+ 713	7
+ 90	3
+ 472	3
+ 47	9
+ 917	8
+ 247	3
+ 869	2
+ 798	8
+ 508	5
+ 798	9
+ 905	2
+ 32	2
+ 714	10
+ 962	6
+ 777	6
+ 705	5
+ 253	8
+ 787	7
+ 67	8
+ 612	10
+ 636	9
+ 298	5
+ 80	1
+ 259	6
+ 563	1
+ 464	5
+ 232	5
+ 625	9
+ 491	6
+ 581	3
+ 157	3
+ 759	4
+ 82	5
+ 136	1
+ 380	7
+ 132	0
+ 607	4
+ 520	7
+ 526	8
+ 275	1
+ 837	7
+ 556	1
+ 235	2
+ 15	7
+ 768	6
+ 994	9
+ 882	8
+ 336	10
+ 299	5
+ 112	7
+ 220	2
+ 695	8
+ 675	2
+ 513	2
+ 995	8
+ 290	8
+ 527	8
+ 900	8
+ 27	9
+ 488	8
+ 510	5
+ 720	4
+ 235	1
+ 355	5
+ 528	5
+ 213	7
+ 711	9
+ 574	4
+ 122	1
+ 587	1
+ 876	9
+ 948	4
+ 723	8
+ 165	7
+ 764	7
+ 545	3
+ 134	3
+ 666	4
+ 321	0
+ 903	8
+ 489	1
+ 597	2
+ 23	2
+ 586	1
+ 259	2
+ 263	1
+ 50	2
+ 537	8
+ 60	7
+ 522	8
+ 354	1
+ 98	5
+ 331	8
+ 857	7
+ 786	8
+ 501	3
+ 876	1
+ 475	9
+ 269	1
+ 45	5
+ 234	3
+ 662	3
+ 518	2
+ 56	6
+ 900	6
+ 402	3
+ 644	5
+ 743	10
+ 264	6
+ 627	1
+ 360	1
+ 325	2
+ 226	8
+ 134	5
+ 861	2
+ 22	1
+ 486	7
+ 379	0
+ 882	4
+ 582	8
+ 12	10
+ 37	7
+ 484	8
+ 631	7
+ 379	3
+ 799	7
+ 387	1
+ 974	6
+ 925	1
+ 108	8
+ 287	1
+ 881	8
+ 813	3
+ 778	7
+ 695	4
+ 478	7
+ 344	5
+ 364	8
+ 294	10
+ 577	7
+ 254	4
+ 412	6
+ 500	4
+ 254	4
+ 495	4
+ 211	8
+ 491	1
+ 555	3
+ 352	3
+ 1000	0
+ 693	5
+ 755	0
+ 992	1
+ 865	3
+ 114	4
+ 959	4
+ 818	4
+ 9	3
+ 757	3
+ 743	3
+ 625	10
+ 34	1
+ 46	6
+ 421	4
+ 923	4
+ 445	6
+ 898	2
+ 653	9
+ 319	5
+ 175	4
+ 960	1
+ 802	8
+ 505	8
+ 96	3
+ 75	8
+ 515	7
+ 793	5
+ 817	8
+ 139	2
+ 236	1
+ 658	7
+ 677	6
+ 882	3
+ 445	2
+ 848	6
+ 635	8
+ 754	4
+ 586	3
+ 249	7
+ 523	3
+ 522	0
+ 24	3
+ 587	8
+ 153	7
+ 78	4
+ 787	7
+ 71	5
+ 291	10
+ 794	7
+ 155	6
+ 356	8
+ 451	1
+ 228	0
+ 370	5
+ 719	9
+ 801	2
+ 930	8
+ 556	5
+ 667	7
+ 242	7
+ 98	0
+ 481	2
+ 493	8
+ 123	3
+ 508	3
+ 929	9
+ 68	4
+ 974	3
+ 417	3
+ 772	1
+ 237	6
+ 378	2
+ 399	9
+ 683	1
+ 642	9
+ 811	7
+ 954	3
+ 910	4
+ 64	0
+ 734	6
+ 310	7
+ 437	4
+ 43	4
+ 674	5
+ 756	4
+ 596	10
+ 20	10
+ 158	4
+ 907	8
+ 485	5
+ 766	3
+ 290	7
+ 588	2
+ 167	7
+ 233	9
+ 224	5
+ 564	7
+ 922	6
+ 73	6
+ 67	8
+ 41	7
+ 820	1
+ 637	10
+ 480	5
+ 820	10
+ 94	6
+ 260	4
+ 306	8
+ 584	5
+ 500	8
+ 374	7
+ 361	9
+ 385	3
+ 545	5
+ 877	6
+ 286	9
+ 275	1
+ 979	9
+ 85	5
+ 457	9
+ 424	6
+ 492	7
+ 936	8
+ 531	5
+ 271	0
+ 337	6
+ 755	7
+ 583	1
+ 980	1
+ 599	9
+ 739	9
+ 776	0
+ 992	8
+ 926	1
+ 215	4
+ 982	6
+ 935	5
+ 322	9
+ 272	9
+ 391	5
+ 885	7
+ 189	6
+ 426	8
+ 780	4
+ 899	4
+ 264	6
+ 264	0
+ 652	3
+ 796	6
+ 332	0
+ 961	3
+ 649	9
+ 789	10
+ 767	1
+ 825	2
+ 605	7
+ 886	8
+ 349	3
+ 566	1
+ 719	5
+ 508	10
+ 103	8
+ 23	8
+ 28	8
+ 333	4
+ 830	3
+ 675	5
+ 190	5
+ 450	10
+ 525	3
+ 115	1
+ 984	0
+ 924	3
+ 313	5
+ 463	0
+ 955	10
+ 15	1
+ 743	0
+ 813	8
+ 858	1
+ 131	7
+ 440	8
+ 167	6
+ 270	6
+ 587	8
+ 892	7
+ 925	9
+ 702	8
+ 210	0
+ 339	7
+ 47	3
+ 643	1
+ 351	4
+ 101	2
+ 157	10
+ 310	3
+ 647	7
+ 93	8
+ 380	4
+ 432	10
+ 158	3
+ 668	1
+ 201	4
+ 933	4
+ 386	3
+ 83	4
+ 566	7
+ 496	9
+ 113	6
+ 81	3
+ 556	4
+ 557	2
+ 140	7
+ 16	5
+ 13	4
+ 487	2
+ 772	2
+ 253	10
+ 526	2
+ 384	9
+ 458	5
+ 345	0
+ 194	8
+ 941	3
+ 438	0
+ 577	10
+ 413	1
+ 196	6
+ 784	2
+ 74	8
+ 660	6
+ 967	4
+ 716	2
+ 405	2
+ 407	8
+ 154	9
+ 256	5
+ 888	4
+ 341	8
+ 757	8
+ 852	3
+ 771	3
+ 468	10
+ 819	3
+ 179	9
+ 49	8
+ 454	0
+ 271	2
+ 238	7
+ 413	6
+ 465	6
+ 509	7
+ 67	4
+ 171	4
+ 226	9
+ 186	1
+ 261	10
+ 343	7
+ 924	2
+ 982	1
+ 55	0
+ 942	5
+ 48	2
+ 679	3
+ 890	1
+ 930	4
+ 659	4
+ 75	7
+ 836	2
+ 133	1
+ 173	3
+ 141	4
+ 277	5
+ 164	2
+ 646	1
+ 305	7
+ 178	2
+ 210	2
+ 460	9
+ 512	4
+ 981	4
+ 705	6
+ 881	8
+ 366	7
+ 26	5
+ 780	2
+ 818	9
+ 634	1
+ 404	8
+ 296	0
+ 945	6
+ 751	1
+ 848	10
+ 349	3
+ 850	9
+ 658	8
+ 303	4
+ 471	2
+ 143	8
+ 902	2
+ 335	7
+ 368	2
+ 602	0
+ 248	0
+ 800	5
+ 55	7
+ 145	8
+ 868	10
+ 767	2
+ 301	6
+ 78	10
+ 447	4
+ 322	9
+ 566	5
+ 754	5
+ 633	1
+ 149	0
+ 242	8
+ 2	5
+ 757	8
+ 35	8
+ 547	2
+ 618	4
+ 174	4
+ 631	5
+ 1	7
+ 434	4
+ 91	8
+ 366	7
+ 221	1
+ 124	9
+ 208	3
+ 855	5
+ 25	9
+ 941	8
+ 660	10
+ 593	2
+ 157	2
+ 621	3
+ 596	3
+ 806	6
+ 962	2
+ 45	1
+ 996	4
+ 709	2
+ 530	8
+ 72	7
+ 107	9
+ 189	1
+ 784	1
+ 913	4
+ 106	5
+ 650	3
+ 717	3
+ 594	3
+ 524	4
+ 910	5
+ 640	10
+ 538	6
+ 365	2
+ 854	9
+ 80	9
+ 634	2
+ 852	8
+ 318	6
+ 953	2
+ 80	1
+ 737	7
+ 323	5
+ 2	9
+ 766	5
+ 317	7
+ 11	10
+ 630	5
+ 593	10
+ 795	4
+ 891	9
+ 372	5
+ 61	2
+ 348	4
+ 861	3
+ 610	9
+ 360	3
+ 672	7
+ 800	7
+ 599	6
+ 199	9
+ 242	2
+ 873	9
+ 759	5
+ 868	6
+ 912	8
+ 429	3
+ 284	5
+ 507	6
+ 869	4
+ 933	5
+ 309	3
+ 825	10
+ 976	6
+ 654	6
+ 190	9
+ 491	4
+ 63	4
+ 304	8
+ 829	2
+ 377	7
+ 931	8
+ 24	2
+ 295	5
+ 848	2
+ 899	8
+ 642	2
+ 74	5
+ 188	0
+ 92	8
+ 624	3
+ 695	1
+ 714	8
+ 479	0
+ 581	3
+ 191	10
+ 49	1
+ 763	1
+ 337	1
+ 604	2
+ 222	5
+ 965	9
+ 712	0
+ 332	9
+ 88	4
+ 742	7
+ 706	4
+ 828	4
+ 196	3
+ 438	8
+ 616	6
+ 735	7
+ 751	5
+ 738	1
+ 556	3
+ 272	8
+ 846	2
+ 643	6
+ 277	10
+ 457	4
+ 398	2
+ 77	1
+ 636	9
+ 524	8
+ 213	10
+ 609	8
+ 591	3
+ 493	3
+ 842	2
+ 430	4
+ 573	7
+ 177	4
+ 940	8
+ 977	2
+ 795	4
+ 581	2
+ 633	7
+ 297	3
+ 564	8
+ 101	8
+ 783	7
+ 605	4
+ 55	1
+ 716	9
+ 329	1
+ 296	9
+ 847	5
+ 321	8
+ 294	3
+ 3	1
+ 731	6
+ 281	4
+ 243	6
+ 634	8
+ 399	7
+ 583	2
+ 445	2
+ 555	5
+ 286	3
+ 398	6
+ 417	7
+ 517	3
+ 167	8
+ 51	5
+ 135	1
+ 549	9
+ 638	8
+ 231	9
+ 409	9
+ 687	8
+ 599	3
+ 989	0
+ 458	5
+ 545	7
+ 816	9
+ 359	2
+ 637	9
+ 496	8
+ 713	5
+ 265	8
+ 601	8
+ 715	2
+ 645	9
+ 119	1
+ 810	8
+ 862	4
+ 76	9
+ 454	5
+ 395	10
+ 279	2
+ 942	6
+ 442	6
+ 513	9
+ 383	2
+ 486	6
+ 73	1
+ 463	8
+ 325	1
+ 733	4
+ 162	5
+ 251	0
+ 952	3
+ 874	4
+ 862	3
+ 405	1
+ 479	3
+ 778	9
+ 925	3
+ 860	3
+ 516	3
+ 956	6
+ 433	4
+ 377	8
+ 527	1
+ 203	7
+ 654	5
+ 713	6
+ 781	6
+ 12	6
+ 856	4
+ 783	3
+ 763	6
+ 257	7
+ 852	1
+ 995	4
+ 463	10
+ 957	9
+ 369	3
+ 654	9
+ 445	9
+ 584	1
+ 310	3
+ 704	1
+ 884	7
+ 734	7
+ 132	5
+ 75	9
+ 79	3
+ 582	9
+ 449	6
+ 299	9
+ 527	3
+ 808	9
+ 590	5
+ 791	0
+ 318	4
+ 134	6
+ 671	8
+ 721	6
+ 554	5
+ 295	7
+ 973	4
+ 582	1
+ 702	2
+ 983	2
+ 741	3
+ 63	3
+ 538	9
+ 163	1
+ 333	10
+ 164	7
+ 329	3
+ 280	10
+ 136	0
+ 555	7
+ 455	8
+ 377	4
+ 220	10
+ 480	9
+ 122	5
+ 72	9
+ 744	1
+ 130	3
+ 6	3
+ 410	3
+ 248	6
+ 989	6
+ 872	3
+ 577	0
+ 270	1
+ 697	7
+ 981	1
+ 153	2
+ 32	6
+ 122	2
+ 96	2
+ 16	8
+ 329	1
+ 122	3
+ 440	5
+ 673	7
+ 107	7
+ 265	10
+ 932	8
+ 986	2
+ 972	7
+ 927	10
+ 757	1
+ 154	8
+ 713	3
+ 942	8
+ 470	10
+ 649	8
+ 104	8
+ 134	5
+ 305	8
+ 232	4
+ 469	5
+ 390	4
+ 338	4
+ 602	3
+ 58	5
+ 264	8
+ 609	4
+ 603	3
+ 694	5
+ 131	2
+ 503	8
+ 962	6
+ 552	1
+ 152	9
+ 902	4
+ 268	4
+ 881	7
+ 772	2
+ 33	4
+ 529	1
+ 903	8
+ 905	5
+ 210	5
+ 833	9
+ 53	10
+ 67	6
+ 744	0
+ 164	3
+ 125	3
+ 153	0
+ 699	4
+ 398	6
+ 78	2
+ 798	1
+ 544	3
+ 202	4
+ 119	1
+ 959	3
+ 615	8
+ 232	7
+ 756	3
+ 224	5
+ 328	4
+ 797	5
+ 703	10
+ 480	4
+ 371	9
+ 982	4
+ 49	8
+ 561	6
+ 106	8
+ 40	2
+ 869	10
+ 554	5
+ 790	8
+ 151	5
+ 85	4
+ 47	4
+ 763	8
+ 866	5
+ 794	3
+ 868	2
+ 225	8
+ 615	3
+ 629	2
+ 866	7
+ 937	9
+ 960	8
+ 904	5
+ 290	7
+ 301	4
+ 241	4
+ 816	3
+ 799	6
+ 131	7
+ 45	9
+ 12	9
+ 90	2
+ 762	7
+ 510	4
+ 880	4
+ 126	8
+ 282	1
+ 623	2
+ 601	9
+ 880	9
+ 354	1
+ 287	2
+ 408	1
+ 749	5
+ 753	8
+ 464	8
+ 707	6
+ 2	5
+ 258	5
+ 859	1
+ 888	10
+ 956	2
+ 71	6
+ 355	7
+ 492	2
+ 574	8
+ 355	9
+ 15	8
+ 948	8
+ 302	7
+ 558	8
+ 466	3
+ 320	5
+ 733	6
+ 980	6
+ 716	9
+ 577	7
+ 37	6
+ 251	4
+ 321	7
+ 627	9
+ 588	10
+ 756	6
+ 746	7
+ 367	0
+ 405	9
+ 814	9
+ 191	1
+ 338	9
+ 712	3
+ 517	4
+ 186	1
+ 100	2
+ 743	4
+ 615	1
+ 93	2
+ 958	7
+ 225	7
+ 284	10
+ 418	7
+ 19	8
+ 577	8
+ 693	8
+ 967	0
+ 692	7
+ 349	2
+ 106	5
+ 303	2
+ 758	0
+ 557	4
+ 109	7
+ 616	1
+ 332	8
+ 782	6
+ 812	2
+ 267	8
+ 22	8
+ 665	7
+ 612	6
+ 746	3
+ 309	1
+ 512	4
+ 630	8
+ 622	4
+ 860	2
+ 762	10
+ 830	4
+ 37	2
+ 219	8
+ 777	0
+ 19	0
+ 863	0
+ 888	5
+ 756	5
+ 159	5
+ 804	5
+ 597	3
+ 884	2
+ 131	5
+ 616	10
+ 685	4
+ 961	5
+ 756	10
+ 675	10
+ 818	5
+ 6	8
+ 496	9
+ 878	4
+ 397	6
+ 884	6
+ 135	7
+ 23	7
+ 3	9
+ 959	1
+ 412	6
+ 125	1
+ 953	1
+ 611	7
+ 84	3
+ 683	9
+ 739	7
+ 738	2
+ 559	6
+ 619	10
+ 249	5
+ 511	4
+ 190	5
+ 116	2
+ 442	1
+ 327	9
+ 649	5
+ 951	6
+ 538	6
+ 310	6
+ 848	10
+ 524	6
+ 684	3
+ 822	2
+ 878	4
+ 198	1
+ 943	7
+ 512	1
+ 244	6
+ 325	7
+ 702	7
+ 539	4
+ 104	5
+ 952	6
+ 52	3
+ 264	9
+ 257	8
+ 487	9
+ 50	3
+ 183	9
+ 748	4
+ 56	7
+ 91	6
+ 823	3
+ 195	1
+ 21	9
+ 801	6
+ 247	9
+ 50	2
+ 546	1
+ 462	8
+ 2	7
+ 597	5
+ 659	6
+ 797	8
+ 575	5
+ 224	6
+ 236	3
+ 198	1
+ 650	4
+ 208	7
+ 289	0
+ 231	5
+ 913	3
+ 735	5
+ 383	2
+ 268	4
+ 915	9
+ 874	6
+ 512	7
+ 417	1
+ 215	6
+ 718	5
+ 955	9
+ 511	6
+ 309	7
+ 275	6
+ 727	5
+ 133	6
+ 786	9
+ 99	2
+ 64	4
+ 554	10
+ 233	4
+ 554	7
+ 98	10
+ 832	3
+ 611	5
+ 765	6
+ 466	3
+ 170	8
+ 995	4
+ 371	7
+ 951	5
+ 363	7
+ 371	5
+ 907	4
+ 830	5
+ 414	1
+ 889	10
+ 808	10
+ 937	6
+ 301	5
+ 189	1
+ 114	7
+ 343	3
+ 429	3
+ 729	8
+ 61	7
+ 304	4
+ 416	7
+ 886	3
+ 110	7
+ 784	5
+ 779	7
+ 491	6
+ 660	4
+ 226	10
+ 976	4
+ 28	1
+ 71	4
+ 374	5
+ 709	1
+ 300	8
+ 782	6
+ 193	2
+ 280	1
+ 521	4
+ 794	3
+ 913	6
+ 978	4
+ 159	6
+ 833	4
+ 600	8
+ 801	6
+ 899	9
+ 999	3
+ 371	7
+ 376	7
+ 477	2
+ 276	7
+ 356	6
+ 749	9
+ 945	5
+ 183	9
+ 116	2
+ 262	3
+ 799	1
+ 661	4
+ 904	5
+ 28	8
+ 334	0
+ 76	7
+ 735	5
+ 376	2
+ 609	7
+ 882	10
+ 207	6
+ 843	2
+ 174	0
+ 10	3
+ 187	3
+ 565	10
+ 366	2
+ 386	3
+ 689	4
+ 73	0
+ 441	1
+ 727	2
+ 600	1
+ 388	2
+ 756	3
+ 176	10
+ 901	0
+ 115	1
+ 45	1
+ 364	2
+ 396	9
+ 218	8
+ 156	6
+ 32	8
+ 18	1
+ 867	5
+ 254	6
+ 635	9
+ 699	0
+ 65	5
+ 293	2
+ 417	2
+ 259	5
+ 268	3
+ 656	6
+ 535	1
+ 562	8
+ 814	7
+ 357	8
+ 563	4
+ 952	4
+ 834	2
+ 25	5
+ 60	7
+ 492	1
+ 178	8
+ 365	6
+ 977	6
+ 127	2
+ 928	8
+ 877	5
+ 834	4
+ 216	6
+ 157	6
+ 495	7
+ 949	4
+ 150	8
+ 653	2
+ 252	7
+ 898	7
+ 838	1
+ 527	2
+ 671	5
+ 827	8
+ 750	8
+ 581	6
+ 217	4
+ 66	4
+ 64	2
+ 7	6
+ 943	10
+ 6	1
+ 738	7
+ 267	10
+ 372	2
+ 733	2
+ 242	3
+ 413	9
+ 765	2
+ 712	5
+ 994	3
+ 142	2
+ 708	2
+ 645	8
+ 431	7
+ 331	4
+ 608	3
+ 466	3
+ 996	7
+ 336	4
+ 899	1
+ 577	1
+ 330	10
+ 54	1
+ 229	8
+ 609	2
+ 59	8
+ 435	8
+ 959	1
+ 539	4
+ 733	9
+ 763	3
+ 207	2
+ 687	2
+ 961	0
+ 570	9
+ 93	1
+ 1	4
+ 137	1
+ 517	4
+ 821	1
+ 590	9
+ 878	0
+ 646	8
+ 106	2
+ 226	8
+ 56	10
+ 180	3
+ 218	9
+ 466	2
+ 891	0
+ 39	10
+ 183	0
+ 407	3
+ 95	9
+ 686	9
+ 51	3
+ 795	9
+ 301	4
+ 765	4
+ 627	10
+ 246	7
+ 981	4
+ 946	2
+ 294	4
+ 378	2
+ 448	4
+ 170	6
+ 457	6
+ 950	6
+ 501	6
+ 467	6
+ 911	3
+ 480	2
+ 704	2
+ 619	3
+ 237	9
+ 14	2
+ 291	10
+ 416	6
+ 372	8
+ 770	8
+ 212	9
+ 451	7
+ 516	4
+ 221	0
+ 37	7
+ 568	9
+ 950	0
+ 160	7
+ 293	8
+ 985	5
+ 644	10
+ 747	9
+ 960	2
+ 519	3
+ 958	3
+ 151	2
+ 227	6
+ 838	7
+ 3	1
+ 760	0
+ 747	3
+ 988	7
+ 376	1
+ 351	7
+ 928	3
+ 198	6
+ 336	9
+ 506	3
+ 109	0
+ 627	1
+ 312	8
+ 236	5
+ 380	1
+ 283	4
+ 133	0
+ 423	9
+ 371	4
+ 577	7
+ 559	9
+ 415	5
+ 264	6
+ 58	6
+ 559	6
+ 896	7
+ 588	5
+ 734	9
+ 302	10
+ 440	7
+ 45	7
+ 66	2
+ 766	5
+ 58	1
+ 899	6
+ 883	5
+ 562	3
+ 945	8
+ 911	0
+ 427	5
+ 566	3
+ 138	2
+ 847	9
+ 55	1
+ 842	5
+ 832	9
+ 218	9
+ 66	10
+ 386	1
+ 120	3
+ 758	0
+ 743	3
+ 300	7
+ 147	2
+ 690	6
+ 681	3
+ 898	8
+ 411	7
+ 691	5
+ 895	5
+ 960	7
+ 420	2
+ 625	5
+ 162	0
+ 610	3
+ 296	4
+ 283	0
+ 688	6
+ 726	8
+ 794	4
+ 410	5
+ 673	3
+ 294	1
+ 54	10
+ 549	9
+ 518	5
+ 677	9
+ 687	3
+ 425	8
+ 314	0
+ 130	6
+ 402	4
+ 648	1
+ 997	4
+ 926	8
+ 791	3
+ 267	5
+ 645	6
+ 547	7
+ 546	1
+ 649	1
+ 605	3
+ 3	3
+ 628	4
+ 141	9
+ 462	3
+ 551	9
+ 684	2
+ 954	7
+ 574	9
+ 472	4
+ 217	7
+ 828	9
+ 299	4
+ 562	8
+ 471	2
+ 909	1
+ 536	9
+ 367	2
+ 339	5
+ 106	8
+ 779	7
+ 664	5
+ 856	6
+ 144	4
+ 499	6
+ 796	7
+ 353	6
+ 579	7
+ 999	1
+ 497	5
+ 351	4
+ 546	9
+ 317	9
+ 51	7
+ 421	2
+ 456	2
+ 813	1
+ 664	7
+ 738	8
+ 100	2
+ 422	9
+ 953	8
+ 520	5
+ 428	5
+ 672	9
+ 990	0
+ 331	5
+ 910	6
+ 448	10
+ 305	9
+ 118	8
+ 70	9
+ 881	7
+ 601	6
+ 541	7
+ 855	10
+ 597	8
+ 739	1
+ 341	2
+ 637	0
+ 93	6
+ 37	4
+ 162	9
+ 73	6
+ 908	4
+ 480	0
+ 139	6
+ 957	0
+ 284	6
+ 638	8
+ 259	5
+ 788	9
+ 302	5
+ 974	6
+ 695	6
+ 656	8
+ 237	7
+ 212	4
+ 639	3
+ 9	5
+ 663	5
+ 573	8
+ 39	5
+ 821	3
+ 88	5
+ 148	3
+ 952	9
+ 204	3
+ 464	2
+ 896	2
+ 789	6
+ 947	0
+ 244	2
+ 425	9
+ 444	4
+ 430	1
+ 924	0
+ 909	10
+ 533	7
+ 286	6
+ 189	4
+ 969	1
+ 370	2
+ 394	8
+ 350	3
+ 993	1
+ 842	9
+ 165	1
+ 99	6
+ 969	5
+ 24	4
+ 651	9
+ 401	6
+ 911	9
+ 290	2
+ 556	5
+ 631	5
+ 619	0
+ 696	0
+ 835	0
+ 303	8
+ 185	1
+ 767	3
+ 231	9
+ 940	2
+ 410	10
+ 598	1
+ 912	10
+ 621	8
+ 934	9
+ 20	5
+ 389	7
+ 14	0
+ 651	7
+ 22	5
+ 757	3
+ 313	9
+ 471	1
+ 292	7
+ 947	2
+ 902	4
+ 196	5
+ 418	1
+ 500	0
+ 931	4
+ 949	10
+ 924	3
+ 601	9
+ 348	3
+ 648	4
+ 738	4
+ 695	1
+ 347	2
+ 132	6
+ 867	1
+ 872	8
+ 436	1
+ 269	9
+ 176	8
+ 893	1
+ 203	8
+ 59	1
+ 181	7
+ 65	5
+ 912	7
+ 898	7
+ 118	6
+ 702	5
+ 758	8
+ 105	6
+ 913	10
+ 395	3
+ 45	7
+ 204	2
+ 433	1
+ 329	6
+ 939	4
+ 764	1
+ 48	8
+ 650	10
+ 542	5
+ 610	7
+ 141	3
+ 126	9
+ 146	2
+ 525	1
+ 208	9
+ 409	3
+ 584	6
+ 474	0
+ 710	8
+ 654	6
+ 190	4
+ 770	2
+ 247	4
+ 198	8
+ 968	8
+ 448	1
+ 121	6
+ 8	3
+ 805	5
+ 326	0
+ 452	7
+ 265	0
+ 347	7
+ 53	1
+ 542	7
+ 706	7
+ 124	5
+ 970	4
+ 896	2
+ 159	9
+ 977	6
+ 972	1
+ 182	10
+ 364	10
+ 513	7
+ 999	10
+ 425	3
+ 1000	8
+ 3	1
+ 829	5
+ 759	5
+ 277	9
+ 12	2
+ 254	9
+ 415	4
+ 772	4
+ 21	7
+ 490	2
+ 725	9
+ 189	2
+ 544	2
+ 202	10
+ 452	2
+ 741	5
+ 254	6
+ 1000	0
+ 106	3
+ 896	1
+ 523	1
+ 27	9
+ 563	8
+ 330	6
+ 544	8
+ 786	3
+ 674	10
+ 506	2
+ 162	7
+ 186	6
+ 910	9
+ 69	2
+ 496	1
+ 177	6
+ 346	1
+ 720	9
+ 223	7
+ 807	8
+ 546	1
+ 369	1
+ 958	2
+ 358	6
+ 129	9
+ 849	3
+ 573	0
+ 906	5
+ 961	10
+ 646	5
+ 45	8
+ 59	4
+ 896	8
+ 259	1
+ 526	1
+ 904	1
+ 204	3
+ 162	2
+ 428	5
+ 793	6
+ 385	6
+ 849	10
+ 676	8
+ 440	6
+ 731	1
+ 94	8
+ 909	2
+ 166	8
+ 933	4
+ 923	5
+ 492	8
+ 531	7
+ 100	7
+ 858	5
+ 214	7
+ 86	6
+ 292	9
+ 556	10
+ 691	10
+ 604	4
+ 82	7
+ 197	10
+ 851	4
+ 796	8
+ 788	7
+ 243	3
+ 547	8
+ 975	6
+ 467	8
+ 176	7
+ 484	3
+ 279	8
+ 198	8
+ 743	9
+ 832	3
+ 310	9
+ 46	5
+ 906	9
+ 871	7
+ 681	7
+ 422	9
+ 938	10
+ 698	9
+ 615	2
+ 747	8
+ 846	2
+ 53	1
+ 6	3
+ 961	7
+ 139	8
+ 97	4
+ 707	1
+ 957	6
+ 40	8
+ 314	7
+ 487	7
+ 645	4
+ 704	3
+ 339	3
+ 508	1
+ 110	4
+ 315	2
+ 479	3
+ 414	4
+ 70	6
+ 231	2
+ 3	9
+ 311	10
+ 550	4
+ 788	9
+ 72	3
+ 600	7
+ 700	3
+ 60	0
+ 623	6
+ 124	7
+ 922	4
+ 897	4
+ 760	3
+ 839	8
+ 864	1
+ 998	9
+ 9	3
+ 827	6
+ 660	6
+ 423	7
+ 891	0
+ 450	6
+ 327	5
+ 630	10
+ 78	8
+ 685	0
+ 194	6
+ 401	10
+ 893	2
+ 785	8
+ 311	8
+ 625	3
+ 92	5
+ 878	8
+ 68	3
+ 484	10
+ 325	9
+ 550	7
+ 444	2
+ 603	5
+ 935	3
+ 522	1
+ 870	9
+ 82	8
+ 163	9
+ 521	5
+ 650	1
+ 794	7
+ 598	7
+ 494	7
+ 974	10
+ 625	3
+ 911	2
+ 951	4
+ 356	6
+ 877	3
+ 842	4
+ 419	7
+ 322	5
+ 476	5
+ 369	10
+ 960	0
+ 143	8
+ 761	7
+ 426	3
+ 408	4
+ 233	0
+ 698	1
+ 209	6
+ 499	6
+ 203	4
+ 856	0
+ 775	3
+ 757	1
+ 776	2
+ 583	1
+ 229	5
+ 164	4
+ 297	9
+ 114	7
+ 180	5
+ 122	4
+ 555	8
+ 556	8
+ 469	1
+ 328	7
+ 431	2
+ 717	2
+ 459	5
+ 302	2
+ 706	9
+ 380	9
+ 428	5
+ 308	7
+ 468	4
+ 447	6
+ 944	6
+ 60	5
+ 390	6
+ 262	9
+ 672	6
+ 531	1
+ 774	2
+ 307	2
+ 721	6
+ 468	4
+ 495	8
+ 363	9
+ 392	7
+ 648	9
+ 93	1
+ 508	0
+ 664	6
+ 536	1
+ 185	8
+ 913	9
+ 390	4
+ 959	2
+ 692	3
+ 397	4
+ 877	9
+ 841	4
+ 713	2
+ 295	1
+ 875	9
+ 965	10
+ 37	5
+ 6	7
+ 42	5
+ 755	2
+ 342	7
+ 84	7
+ 112	0
+ 895	8
+ 310	3
+ 218	2
+ 158	1
+ 559	9
+ 264	9
+ 976	1
+ 796	9
+ 108	8
+ 413	1
+ 533	5
+ 658	3
+ 682	10
+ 956	8
+ 731	1
+ 809	6
+ 873	1
+ 918	1
+ 307	1
+ 152	9
+ 947	4
+ 719	9
+ 555	5
+ 863	7
+ 347	3
+ 778	9
+ 731	4
+ 168	4
+ 435	1
+ 179	2
+ 193	10
+ 791	1
+ 108	7
+ 159	4
+ 786	3
+ 280	7
+ 726	10
+ 655	3
+ 512	5
+ 944	9
+ 793	7
+ 739	5
+ 158	9
+ 937	6
+ 32	1
+ 758	2
+ 104	5
+ 292	2
+ 259	5
+ 626	0
+ 761	9
+ 777	5
+ 903	4
+ 767	4
+ 949	7
+ 274	7
+ 434	0
+ 266	6
+ 921	2
+ 184	10
+ 318	9
+ 178	4
+ 491	5
+ 633	8
+ 921	3
+ 795	7
+ 164	6
+ 168	1
+ 3	9
+ 483	10
+ 647	8
+ 694	1
+ 771	10
+ 673	7
+ 163	9
+ 644	5
+ 799	8
+ 903	3
+ 292	5
+ 40	2
+ 794	8
+ 895	10
+ 407	1
+ 25	4
+ 999	5
+ 362	6
+ 265	1
+ 727	0
+ 16	4
+ 727	2
+ 257	4
+ 660	1
+ 193	6
+ 345	5
+ 98	4
+ 698	9
+ 221	6
+ 850	6
+ 656	9
+ 37	7
+ 383	4
+ 301	6
+ 455	0
+ 684	5
+ 428	4
+ 650	7
+ 781	3
+ 740	10
+ 872	1
+ 459	10
+ 471	2
+ 863	7
+ 749	7
+ 319	4
+ 589	4
+ 59	10
+ 755	4
+ 621	2
+ 388	3
+ 681	8
+ 716	3
+ 501	5
+ 641	2
+ 471	5
+ 327	9
+ 484	8
+ 87	3
+ 490	8
+ 60	8
+ 241	6
+ 166	3
+ 622	9
+ 661	2
+ 132	0
+ 547	8
+ 865	3
+ 144	4
+ 760	8
+ 606	2
+ 299	9
+ 162	8
+ 731	2
+ 130	2
+ 85	2
+ 30	3
+ 840	2
+ 627	5
+ 117	3
+ 705	2
+ 337	3
+ 61	2
+ 515	2
+ 566	3
+ 991	2
+ 506	3
+ 105	7
+ 74	8
+ 916	2
+ 57	0
+ 395	1
+ 328	2
+ 282	10
+ 697	4
+ 243	4
+ 647	6
+ 654	7
+ 781	2
+ 914	3
+ 444	9
+ 520	9
+ 196	6
+ 618	3
+ 461	5
+ 474	5
+ 535	9
+ 604	9
+ 105	9
+ 818	8
+ 285	1
+ 205	9
+ 641	9
+ 641	4
+ 28	6
+ 768	5
+ 461	3
+ 421	7
+ 913	0
+ 927	4
+ 573	4
+ 892	1
+ 271	5
+ 971	4
+ 382	8
+ 179	7
+ 851	4
+ 600	5
+ 243	2
+ 913	3
+ 796	7
+ 742	3
+ 969	2
+ 914	9
+ 202	8
+ 257	8
+ 243	1
+ 882	5
+ 644	9
+ 891	0
+ 643	1
+ 694	5
+ 454	3
+ 986	7
+ 535	9
+ 967	3
+ 580	7
+ 588	5
+ 871	5
+ 432	1
+ 344	7
+ 847	6
+ 837	7
+ 100	5
+ 583	10
+ 508	2
+ 61	2
+ 721	5
+ 497	7
+ 211	0
+ 606	2
+ 363	2
+ 887	10
+ 736	8
+ 454	2
+ 831	8
+ 858	7
+ 384	7
+ 408	5
+ 176	10
+ 475	7
+ 218	5
+ 887	9
+ 51	4
+ 646	3
+ 415	3
+ 440	8
+ 438	3
+ 729	2
+ 86	2
+ 344	9
+ 981	2
+ 596	4
+ 896	0
+ 850	1
+ 995	3
+ 756	2
+ 861	6
+ 152	9
+ 26	8
+ 174	4
+ 50	6
+ 218	5
+ 942	9
+ 663	0
+ 131	0
+ 944	1
+ 208	5
+ 477	1
+ 544	3
+ 176	5
+ 652	9
+ 752	5
+ 574	9
+ 424	6
+ 702	6
+ 41	8
+ 212	3
+ 241	2
+ 207	9
+ 181	3
+ 911	1
+ 450	1
+ 665	9
+ 222	2
+ 254	4
+ 748	9
+ 329	5
+ 418	9
+ 405	8
+ 504	1
+ 441	5
+ 860	7
+ 803	1
+ 807	0
+ 4	10
+ 348	9
+ 114	8
+ 33	8
+ 725	3
+ 988	10
+ 653	7
+ 885	10
+ 238	3
+ 886	6
+ 146	4
+ 751	6
+ 934	6
+ 240	7
+ 712	0
+ 748	7
+ 35	1
+ 631	1
+ 894	7
+ 928	6
+ 920	9
+ 598	6
+ 654	5
+ 556	9
+ 786	4
+ 535	9
+ 832	3
+ 518	8
+ 896	8
+ 504	6
+ 804	3
+ 324	8
+ 347	10
+ 989	2
+ 619	9
+ 860	5
+ 834	5
+ 113	5
+ 942	7
+ 380	7
+ 112	9
+ 659	9
+ 200	2
+ 711	1
+ 934	2
+ 704	7
+ 466	0
+ 578	8
+ 983	6
+ 55	6
+ 485	9
+ 142	3
+ 373	3
+ 808	3
+ 925	2
+ 43	0
+ 102	7
+ 981	3
+ 878	7
+ 398	8
+ 906	1
+ 551	4
+ 129	1
+ 186	1
+ 697	2
+ 715	2
+ 156	9
+ 502	5
+ 112	3
+ 844	0
+ 497	9
+ 74	6
+ 589	1
+ 900	5
+ 747	3
+ 280	7
+ 399	8
+ 26	5
+ 961	2
+ 641	7
+ 453	4
+ 840	6
+ 212	3
+ 138	3
+ 651	10
+ 362	1
+ 869	4
+ 746	5
+ 490	6
+ 925	2
+ 943	2
+ 890	3
+ 36	9
+ 870	10
+ 128	5
+ 655	6
+ 866	5
+ 190	1
+ 837	3
+ 403	5
+ 310	8
+ 636	2
+ 200	4
+ 637	7
+ 28	6
+ 927	10
+ 766	8
+ 313	8
+ 733	2
+ 798	9
+ 695	5
+ 443	6
+ 948	6
+ 640	8
+ 960	0
+ 274	3
+ 808	9
+ 449	0
+ 292	1
+ 698	3
+ 648	6
+ 291	4
+ 443	6
+ 215	2
+ 788	0
+ 37	5
+ 467	5
+ 44	4
+ 112	7
+ 200	1
+ 727	5
+ 342	5
+ 383	8
+ 542	7
+ 877	2
+ 995	5
+ 866	3
+ 938	3
+ 891	2
+ 484	7
+ 167	5
+ 162	6
+ 1	2
+ 48	1
+ 890	2
+ 186	6
+ 721	5
+ 151	1
+ 318	7
+ 779	2
+ 934	8
+ 719	8
+ 61	7
+ 108	10
+ 810	6
+ 632	10
+ 114	8
+ 610	1
+ 0	7
+ 229	9
+ 906	4
+ 506	6
+ 942	7
+ 731	3
+ 350	5
+ 455	3
+ 284	2
+ 83	3
+ 830	2
+ 297	6
+ 783	9
+ 617	9
+ 723	2
+ 12	7
+ 885	2
+ 614	8
+ 656	1
+ 418	6
+ 777	1
+ 858	1
+ 659	3
+ 411	9
+ 486	5
+ 288	3
+ 685	6
+ 957	5
+ 514	6
+ 365	2
+ 801	4
+ 961	7
+ 618	6
+ 477	3
+ 695	9
+ 871	5
+ 44	7
+ 600	7
+ 42	0
+ 646	5
+ 504	9
+ 845	2
+ 520	8
+ 657	0
+ 375	0
+ 272	2
+ 398	2
+ 862	0
+ 809	3
+ 290	5
+ 234	2
+ 976	3
+ 891	6
+ 982	9
+ 587	6
+ 461	1
+ 563	3
+ 280	1
+ 107	9
+ 117	5
+ 958	4
+ 658	4
+ 623	5
+ 372	4
+ 859	7
+ 935	1
+ 823	9
+ 372	7
+ 488	4
+ 646	1
+ 982	1
+ 165	5
+ 412	4
+ 628	5
+ 382	7
+ 2	3
+ 135	7
+ 696	8
+ 179	1
+ 190	0
+ 730	1
+ 131	6
+ 36	5
+ 266	5
+ 857	9
+ 598	8
+ 19	8
+ 384	4
+ 209	0
+ 951	6
+ 759	10
+ 932	9
+ 612	6
+ 652	8
+ 696	8
+ 830	4
+ 966	10
+ 978	0
+ 464	2
+ 527	3
+ 155	1
+ 160	2
+ 889	5
+ 605	1
+ 556	6
+ 690	3
+ 508	6
+ 209	1
+ 249	9
+ 912	9
+ 703	7
+ 370	7
+ 703	3
+ 672	2
+ 591	2
+ 488	7
+ 324	6
+ 921	2
+ 191	5
+ 311	7
+ 82	0
+ 62	6
+ 621	3
+ 710	9
+ 132	6
+ 815	8
+ 364	2
+ 504	1
+ 533	2
+ 234	1
+ 374	7
+ 872	7
+ 369	8
+ 911	6
+ 319	2
+ 307	4
+ 221	4
+ 990	8
+ 641	7
+ 713	8
+ 323	5
+ 608	7
+ 714	1
+ 755	2
+ 288	10
+ 372	7
+ 711	2
+ 360	1
+ 36	3
+ 640	4
+ 492	9
+ 755	7
+ 317	7
+ 556	10
+ 446	3
+ 731	8
+ 798	3
+ 457	5
+ 450	2
+ 760	7
+ 201	1
+ 400	9
+ 375	8
+ 991	4
+ 31	6
+ 765	5
+ 578	5
+ 237	9
+ 265	8
+ 852	7
+ 63	6
+ 481	9
+ 921	9
+ 374	4
+ 149	1
+ 109	3
+ 265	5
+ 261	6
+ 270	3
+ 50	3
+ 883	8
+ 824	5
+ 335	1
+ 355	6
+ 854	2
+ 312	9
+ 789	8
+ 778	7
+ 730	2
+ 81	9
+ 285	2
+ 228	6
+ 700	5
+ 190	10
+ 740	2
+ 271	6
+ 55	1
+ 84	4
+ 156	4
+ 990	0
+ 646	3
+ 927	4
+ 94	7
+ 145	8
+ 856	2
+ 702	1
+ 417	9
+ 692	1
+ 418	9
+ 87	2
+ 122	4
+ 782	2
+ 453	9
+ 567	6
+ 305	6
+ 619	10
+ 859	5
+ 386	10
+ 250	5
+ 776	1
+ 757	5
+ 249	2
+ 407	9
+ 291	8
+ 823	4
+ 983	9
+ 736	8
+ 121	2
+ 631	7
+ 798	9
+ 245	4
+ 886	1
+ 963	3
+ 57	2
+ 803	8
+ 320	6
+ 310	6
+ 734	7
+ 509	0
+ 543	3
+ 403	5
+ 276	1
+ 291	4
+ 328	9
+ 85	1
+ 858	3
+ 544	7
+ 434	5
+ 16	5
+ 719	8
+ 324	0
+ 378	6
+ 607	1
+ 352	1
+ 137	9
+ 447	5
+ 420	7
+ 679	7
+ 119	0
+ 634	2
+ 134	5
+ 535	7
+ 236	10
+ 184	3
+ 461	9
+ 71	8
+ 942	4
+ 418	5
+ 561	8
+ 664	7
+ 664	1
+ 238	1
+ 834	9
+ 796	10
+ 924	4
+ 158	1
+ 921	7
+ 735	2
+ 662	9
+ 409	1
+ 822	5
+ 907	8
+ 929	3
+ 312	5
+ 95	10
+ 188	8
+ 87	4
+ 844	9
+ 342	6
+ 874	3
+ 69	0
+ 324	10
+ 724	1
+ 148	4
+ 977	6
+ 510	8
+ 38	4
+ 563	10
+ 743	9
+ 458	8
+ 851	6
+ 598	9
+ 72	4
+ 859	4
+ 81	7
+ 680	2
+ 764	0
+ 141	5
+ 63	3
+ 875	0
+ 846	4
+ 839	9
+ 801	4
+ 851	5
+ 277	3
+ 382	1
+ 955	10
+ 65	0
+ 421	9
+ 441	5
+ 656	1
+ 653	4
+ 125	8
+ 908	2
+ 83	8
+ 228	9
+ 167	1
+ 813	10
+ 469	7
+ 513	7
+ 974	9
+ 873	9
+ 875	9
+ 955	3
+ 862	4
+ 799	5
+ 517	5
+ 939	6
+ 245	8
+ 830	3
+ 630	1
+ 257	8
+ 126	1
+ 765	6
+ 734	3
+ 341	7
+ 173	2
+ 637	0
+ 152	6
+ 344	0
+ 988	1
+ 533	5
+ 594	5
+ 148	8
+ 319	10
+ 166	9
+ 37	4
+ 745	2
+ 493	5
+ 757	2
+ 788	1
+ 935	10
+ 311	6
+ 9	5
+ 164	4
+ 478	2
+ 495	0
+ 658	1
+ 483	8
+ 927	8
+ 785	1
+ 751	8
+ 516	5
+ 984	0
+ 7	7
+ 235	8
+ 840	2
+ 756	2
+ 742	8
+ 615	9
+ 118	1
+ 58	6
+ 104	7
+ 700	6
+ 522	6
+ 389	3
+ 720	1
+ 128	2
+ 637	1
+ 244	6
+ 854	5
+ 439	7
+ 650	2
+ 845	4
+ 961	5
+ 298	1
+ 552	4
+ 690	7
+ 72	4
+ 243	6
+ 18	6
+ 901	7
+ 772	0
+ 973	4
+ 142	2
+ 52	10
+ 695	5
+ 691	3
+ 687	5
+ 737	6
+ 995	0
+ 725	5
+ 392	4
+ 203	5
+ 806	4
+ 59	8
+ 77	10
+ 562	8
+ 989	5
+ 258	1
+ 751	3
+ 127	4
+ 802	8
+ 792	5
+ 353	5
+ 136	3
+ 564	9
+ 895	10
+ 278	1
+ 420	1
+ 544	5
+ 908	6
+ 438	5
+ 471	4
+ 5	7
+ 558	8
+ 40	7
+ 203	8
+ 503	10
+ 331	9
+ 523	5
+ 205	1
+ 330	1
+ 42	6
+ 199	5
+ 692	7
+ 941	6
+ 363	4
+ 70	8
+ 806	1
+ 563	4
+ 831	6
+ 49	0
+ 445	6
+ 28	8
+ 408	6
+ 245	6
+ 638	6
+ 713	7
+ 182	9
+ 142	9
+ 654	1
+ 473	0
+ 463	5
+ 852	3
+ 619	4
+ 632	4
+ 18	7
+ 484	5
+ 233	5
+ 240	6
+ 63	5
+ 254	7
+ 59	10
+ 380	2
+ 880	5
+ 114	5
+ 606	6
+ 551	1
+ 131	4
+ 337	7
+ 818	10
+ 199	8
+ 650	7
+ 299	9
+ 195	5
+ 524	3
+ 23	8
+ 958	1
+ 746	3
+ 322	6
+ 860	4
+ 159	5
+ 23	7
+ 535	2
+ 114	9
+ 904	9
+ 842	1
+ 767	5
+ 786	1
+ 375	10
+ 604	9
+ 239	6
+ 678	2
+ 708	4
+ 534	0
+ 49	4
+ 466	2
+ 861	5
+ 919	4
+ 643	0
+ 269	5
+ 964	1
+ 650	7
+ 603	4
+ 797	10
+ 418	4
+ 877	7
+ 28	6
+ 853	7
+ 979	4
+ 766	0
+ 782	2
+ 236	6
+ 721	2
+ 40	4
+ 188	3
+ 911	2
+ 419	6
+ 884	0
+ 999	7
+ 1000	4
+ 82	9
+ 73	1
+ 432	9
+ 846	4
+ 313	6
+ 439	1
+ 844	7
+ 739	6
+ 831	8
+ 929	0
+ 87	8
+ 172	5
+ 402	1
+ 528	4
+ 736	5
+ 817	8
+ 405	9
+ 927	8
+ 817	8
+ 249	1
+ 384	7
+ 225	2
+ 364	10
+ 793	2
+ 742	7
+ 215	8
+ 562	4
+ 336	10
+ 443	9
+ 365	2
+ 392	2
+ 997	8
+ 72	9
+ 635	9
+ 697	9
+ 19	1
+ 573	2
+ 309	9
+ 208	1
+ 132	10
+ 824	3
+ 780	4
+ 734	1
+ 351	2
+ 980	7
+ 356	4
+ 897	4
+ 170	10
+ 276	8
+ 858	10
+ 690	9
+ 54	3
+ 121	4
+ 199	3
+ 466	3
+ 280	3
+ 678	1
+ 677	4
+ 175	0
+ 589	2
+ 743	9
+ 527	6
+ 297	7
+ 610	6
+ 502	5
+ 547	2
+ 345	6
+ 454	5
+ 965	7
+ 795	4
+ 983	1
+ 721	7
+ 135	4
+ 74	3
+ 425	7
+ 465	2
+ 607	10
+ 808	9
+ 689	4
+ 478	2
+ 886	0
+ 382	2
+ 626	8
+ 697	6
+ 488	5
+ 21	5
+ 567	7
+ 133	7
+ 140	2
+ 12	6
+ 869	5
+ 734	5
+ 469	5
+ 381	2
+ 960	9
+ 349	8
+ 884	7
+ 77	5
+ 567	8
+ 100	1
+ 266	1
+ 527	8
+ 864	7
+ 535	0
+ 867	5
+ 570	7
+ 24	3
+ 213	5
+ 845	6
+ 651	8
+ 453	0
+ 651	3
+ 732	7
+ 846	3
+ 501	9
+ 355	8
+ 67	9
+ 600	9
+ 542	1
+ 935	4
+ 682	5
+ 146	7
+ 808	4
+ 199	7
+ 953	9
+ 459	4
+ 851	1
+ 743	6
+ 837	6
+ 882	3
+ 534	2
+ 105	6
+ 118	7
+ 532	7
+ 840	5
+ 70	5
+ 971	2
+ 228	8
+ 575	4
+ 433	5
+ 277	9
+ 935	1
+ 1	7
+ 710	8
+ 266	6
+ 176	8
+ 828	3
+ 402	9
+ 986	9
+ 607	8
+ 399	7
+ 348	4
+ 892	6
+ 150	5
+ 1	6
+ 996	3
+ 474	9
+ 406	5
+ 609	1
+ 312	9
+ 708	5
+ 676	5
+ 768	1
+ 483	8
+ 10	1
+ 580	4
+ 766	9
+ 780	7
+ 502	9
+ 125	5
+ 513	1
+ 782	10
+ 52	2
+ 461	7
+ 304	8
+ 535	0
+ 261	2
+ 548	0
+ 288	0
+ 783	3
+ 121	4
+ 708	9
+ 290	5
+ 545	8
+ 418	7
+ 296	9
+ 791	1
+ 918	8
+ 266	4
+ 504	6
+ 153	0
+ 581	4
+ 250	1
+ 443	5
+ 161	2
+ 836	3
+ 589	5
+ 169	9
+ 32	7
+ 671	4
+ 384	10
+ 381	2
+ 45	3
+ 19	3
+ 678	5
+ 881	8
+ 561	5
+ 244	8
+ 591	7
+ 349	8
+ 913	2
+ 34	5
+ 728	2
+ 380	8
+ 916	1
+ 209	3
+ 18	6
+ 476	1
+ 890	5
+ 374	6
+ 17	3
+ 399	6
+ 716	6
+ 389	3
+ 330	7
+ 60	2
+ 922	1
+ 744	6
+ 296	1
+ 409	2
+ 174	6
+ 513	2
+ 209	10
+ 255	1
+ 483	6
+ 667	5
+ 883	1
+ 78	6
+ 708	5
+ 907	0
+ 204	10
+ 280	1
+ 60	0
+ 775	4
+ 147	2
+ 569	3
+ 803	1
+ 512	0
+ 71	8
+ 111	6
+ 396	8
+ 53	3
+ 842	1
+ 878	6
+ 597	8
+ 588	8
+ 751	9
+ 927	8
+ 891	7
+ 169	0
+ 886	7
+ 359	7
+ 820	9
+ 701	9
+ 638	8
+ 445	0
+ 588	5
+ 312	4
+ 628	2
+ 981	2
+ 975	6
+ 26	7
+ 437	10
+ 538	3
+ 655	7
+ 366	5
+ 445	7
+ 229	3
+ 595	9
+ 156	2
+ 741	6
+ 266	3
+ 98	6
+ 760	7
+ 768	7
+ 952	7
+ 310	10
+ 469	7
+ 931	0
+ 74	6
+ 713	4
+ 127	2
+ 164	4
+ 423	8
+ 286	6
+ 992	0
+ 180	3
+ 357	3
+ 837	1
+ 5	6
+ 858	10
+ 348	2
+ 935	8
+ 915	9
+ 823	10
+ 452	5
+ 429	6
+ 694	6
+ 935	1
+ 352	2
+ 696	3
+ 249	9
+ 602	6
+ 153	4
+ 722	2
+ 44	6
+ 115	4
+ 748	0
+ 208	7
+ 915	0
+ 652	4
+ 566	1
+ 946	3
+ 673	9
+ 377	0
+ 102	1
+ 369	4
+ 948	10
+ 956	1
+ 409	7
+ 259	5
+ 258	4
+ 844	0
+ 423	1
+ 669	3
+ 82	3
+ 705	6
+ 402	7
+ 908	1
+ 533	3
+ 101	6
+ 357	5
+ 986	3
+ 440	9
+ 406	8
+ 620	7
+ 303	9
+ 39	1
+ 885	5
+ 199	6
+ 801	3
+ 875	5
+ 929	3
+ 157	8
+ 353	7
+ 123	5
+ 325	5
+ 923	3
+ 785	4
+ 252	2
+ 213	9
+ 857	5
+ 751	9
+ 663	6
+ 359	9
+ 190	2
+ 142	1
+ 665	1
+ 343	8
+ 909	7
+ 513	0
+ 149	8
+ 513	1
+ 148	3
+ 435	4
+ 489	6
+ 273	3
+ 163	0
+ 243	8
+ 660	6
+ 687	8
+ 761	8
+ 914	4
+ 901	3
+ 249	8
+ 952	8
+ 843	1
+ 600	4
+ 173	7
+ 653	6
+ 149	1
+ 255	4
+ 489	4
+ 446	7
+ 244	1
+ 334	9
+ 955	1
+ 760	9
+ 521	7
+ 126	8
+ 471	1
+ 532	3
+ 180	1
+ 668	4
+ 880	3
+ 961	0
+ 464	2
+ 450	10
+ 634	9
+ 685	9
+ 2	0
+ 809	10
+ 113	6
+ 826	6
+ 230	10
+ 405	7
+ 30	9
+ 14	2
+ 69	7
+ 563	9
+ 3	5
+ 978	5
+ 740	4
+ 420	4
+ 324	1
+ 252	3
+ 123	1
+ 283	2
+ 631	1
+ 871	9
+ 60	3
+ 561	1
+ 213	6
+ 301	3
+ 257	9
+ 232	3
+ 388	2
+ 727	1
+ 637	1
+ 501	10
+ 252	8
+ 288	4
+ 815	6
+ 612	4
+ 678	5
+ 306	7
+ 759	9
+ 829	10
+ 442	1
+ 255	7
+ 994	5
+ 959	4
+ 696	7
+ 509	3
+ 833	0
+ 294	1
+ 764	6
+ 461	6
+ 152	1
+ 25	8
+ 555	3
+ 569	3
+ 199	4
+ 287	6
+ 528	5
+ 339	5
+ 28	3
+ 903	7
+ 983	4
+ 57	8
+ 422	4
+ 902	2
+ 933	4
+ 765	1
+ 435	8
+ 915	10
+ 122	5
+ 304	3
+ 882	6
+ 961	4
+ 133	3
+ 931	2
+ 598	8
+ 885	6
+ 246	9
+ 397	7
+ 292	3
+ 853	2
+ 662	6
+ 310	1
+ 409	2
+ 86	5
+ 709	4
+ 852	6
+ 982	8
+ 1	1
+ 114	9
+ 276	7
+ 766	2
+ 293	0
+ 102	7
+ 680	4
+ 989	5
+ 620	7
+ 152	9
+ 747	6
+ 154	8
+ 92	9
+ 224	9
+ 454	2
+ 758	5
+ 321	9
+ 386	6
+ 584	2
+ 758	9
+ 164	9
+ 567	8
+ 255	6
+ 377	9
+ 207	5
+ 804	10
+ 89	10
+ 788	2
+ 821	0
+ 126	3
+ 218	9
+ 729	5
+ 757	1
+ 136	3
+ 267	9
+ 219	4
+ 755	8
+ 275	0
+ 342	7
+ 885	5
+ 179	7
+ 503	3
+ 648	3
+ 450	5
+ 303	6
+ 743	5
+ 460	5
+ 60	2
+ 587	2
+ 559	9
+ 91	8
+ 285	8
+ 563	6
+ 856	9
+ 211	7
+ 454	4
+ 430	10
+ 659	1
+ 249	1
+ 546	6
+ 685	3
+ 72	1
+ 762	1
+ 363	3
+ 328	9
+ 202	4
+ 699	5
+ 265	3
+ 47	1
+ 168	3
+ 862	6
+ 649	3
+ 580	3
+ 369	8
+ 417	9
+ 379	1
+ 205	5
+ 247	10
+ 583	6
+ 315	9
+ 532	5
+ 331	2
+ 5	6
+ 493	1
+ 717	7
+ 310	6
+ 283	10
+ 870	9
+ 267	2
+ 691	7
+ 154	1
+ 786	4
+ 522	0
+ 326	1
+ 642	6
+ 17	2
+ 158	3
+ 405	2
+ 943	9
+ 215	7
+ 559	5
+ 238	8
+ 484	1
+ 704	8
+ 346	4
+ 435	5
+ 465	2
+ 860	10
+ 253	2
+ 92	9
+ 826	1
+ 70	10
+ 456	5
+ 147	4
+ 373	4
+ 60	9
+ 887	3
+ 774	4
+ 405	5
+ 122	8
+ 873	6
+ 253	3
+ 778	1
+ 326	0
+ 298	4
+ 927	1
+ 527	10
+ 109	10
+ 471	3
+ 383	8
+ 618	4
+ 775	5
+ 740	5
+ 875	1
+ 27	10
+ 897	9
+ 554	1
+ 239	3
+ 263	6
+ 362	6
+ 982	3
+ 686	5
+ 285	8
+ 492	8
+ 51	9
+ 600	7
+ 317	4
+ 173	1
+ 924	0
+ 203	10
+ 45	1
+ 851	6
+ 250	1
+ 930	5
+ 654	3
+ 74	6
+ 581	8
+ 145	9
+ 554	6
+ 623	6
+ 511	2
+ 274	8
+ 598	4
+ 886	5
+ 496	1
+ 474	5
+ 189	3
+ 141	4
+ 414	1
+ 953	1
+ 363	0
+ 704	9
+ 786	8
+ 811	3
+ 485	4
+ 946	10
+ 657	2
+ 825	3
+ 668	7
+ 778	2
+ 800	3
+ 705	10
+ 576	8
+ 429	10
+ 916	4
+ 58	3
+ 409	8
+ 225	2
+ 610	0
+ 536	1
+ 470	5
+ 92	1
+ 702	9
+ 383	4
+ 628	2
+ 533	4
+ 412	2
+ 417	10
+ 84	8
+ 978	0
+ 229	0
+ 280	6
+ 798	5
+ 834	4
+ 540	4
+ 504	0
+ 852	6
+ 138	6
+ 512	5
+ 925	1
+ 682	5
+ 567	1
+ 696	10
+ 82	8
+ 830	1
+ 780	1
+ 96	1
+ 697	9
+ 565	5
+ 302	1
+ 900	8
+ 116	8
+ 401	3
+ 307	9
+ 774	2
+ 52	5
+ 690	6
+ 550	4
+ 603	6
+ 166	4
+ 691	9
+ 493	8
+ 7	2
+ 681	6
+ 720	10
+ 677	6
+ 789	8
+ 374	2
+ 46	7
+ 103	8
+ 913	2
+ 276	6
+ 774	8
+ 989	4
+ 457	2
+ 811	1
+ 102	3
+ 935	1
+ 493	6
+ 680	2
+ 601	4
+ 835	4
+ 149	2
+ 580	2
+ 889	7
+ 14	8
+ 838	3
+ 404	6
+ 115	4
+ 989	6
+ 548	8
+ 720	6
+ 103	7
+ 758	6
+ 272	4
+ 810	9
+ 795	6
+ 262	9
+ 852	8
+ 138	7
+ 525	2
+ 543	4
+ 442	9
+ 975	6
+ 340	10
+ 129	9
+ 764	8
+ 537	9
+ 504	3
+ 463	8
+ 733	3
+ 649	5
+ 916	9
+ 471	8
+ 754	6
+ 510	3
+ 761	1
+ 642	2
+ 1000	6
+ 761	3
+ 581	9
+ 227	3
+ 740	8
+ 211	2
+ 58	7
+ 21	8
+ 946	7
+ 318	9
+ 582	8
+ 631	3
+ 398	1
+ 615	2
+ 194	3
+ 363	2
+ 875	1
+ 533	5
+ 16	8
+ 801	8
+ 522	0
+ 0	6
+ 686	0
+ 371	6
+ 692	7
+ 494	3
+ 478	1
+ 610	9
+ 266	2
+ 36	5
+ 483	4
+ 653	4
+ 524	2
+ 814	5
+ 945	6
+ 296	5
+ 627	3
+ 47	3
+ 318	4
+ 944	0
+ 108	4
+ 283	6
+ 564	9
+ 463	8
+ 118	5
+ 290	6
+ 898	9
+ 959	4
+ 129	8
+ 963	1
+ 388	3
+ 541	0
+ 555	6
+ 327	9
+ 6	3
+ 882	1
+ 711	2
+ 700	3
+ 58	2
+ 105	2
+ 662	4
+ 777	6
+ 338	7
+ 983	5
+ 509	9
+ 540	9
+ 205	1
+ 912	8
+ 669	2
+ 633	7
+ 511	5
+ 790	2
+ 680	5
+ 496	7
+ 653	6
+ 915	3
+ 995	7
+ 875	3
+ 429	9
+ 800	9
+ 804	3
+ 835	0
+ 422	7
+ 768	1
+ 987	4
+ 767	5
+ 915	6
+ 720	6
+ 47	2
+ 334	7
+ 817	2
+ 15	8
+ 941	9
+ 145	4
+ 747	9
+ 307	6
+ 286	1
+ 559	7
+ 890	3
+ 798	9
+ 727	6
+ 375	6
+ 122	1
+ 238	2
+ 311	6
+ 869	1
+ 820	9
+ 941	8
+ 773	1
+ 130	5
+ 31	4
+ 70	3
+ 580	6
+ 24	5
+ 956	8
+ 347	7
+ 387	7
+ 325	5
+ 817	6
+ 678	1
+ 134	5
+ 257	10
+ 431	2
+ 715	2
+ 284	8
+ 724	3
+ 281	8
+ 632	9
+ 423	7
+ 331	4
+ 477	7
+ 62	9
+ 400	4
+ 374	2
+ 950	1
+ 346	1
+ 599	6
+ 38	0
+ 800	8
+ 234	1
+ 597	10
+ 399	9
+ 751	0
+ 740	2
+ 686	1
+ 554	2
+ 749	6
+ 28	1
+ 2	4
+ 366	10
+ 454	7
+ 36	1
+ 314	1
+ 83	1
+ 827	3
+ 198	4
+ 275	6
+ 304	0
+ 628	0
+ 201	3
+ 114	8
+ 477	9
+ 370	5
+ 12	4
+ 907	4
+ 324	4
+ 89	4
+ 414	4
+ 435	5
+ 517	3
+ 815	7
+ 687	1
+ 313	10
+ 116	9
+ 34	3
+ 255	1
+ 71	7
+ 12	4
+ 237	0
+ 812	1
+ 401	1
+ 505	5
+ 496	9
+ 893	9
+ 417	4
+ 193	2
+ 125	9
+ 321	4
+ 871	4
+ 380	9
+ 753	6
+ 53	8
+ 366	1
+ 264	6
+ 88	1
+ 747	5
+ 213	3
+ 979	7
+ 171	9
+ 640	6
+ 281	8
+ 819	4
+ 714	1
+ 845	6
+ 577	2
+ 489	3
+ 859	5
+ 154	2
+ 607	4
+ 828	7
+ 495	6
+ 184	7
+ 827	2
+ 417	10
+ 34	1
+ 586	3
+ 890	4
+ 721	6
+ 545	6
+ 188	1
+ 791	7
+ 452	7
+ 219	6
+ 875	8
+ 25	7
+ 521	5
+ 279	7
+ 228	1
+ 868	6
+ 105	9
+ 701	7
+ 217	6
+ 96	9
+ 196	6
+ 505	4
+ 763	3
+ 61	2
+ 946	3
+ 823	8
+ 107	8
+ 525	6
+ 368	8
+ 333	6
+ 910	2
+ 240	0
+ 103	9
+ 706	3
+ 533	8
+ 258	7
+ 443	8
+ 112	2
+ 58	2
+ 423	0
+ 455	2
+ 825	6
+ 93	4
+ 190	5
+ 154	5
+ 56	1
+ 725	3
+ 79	8
+ 237	8
+ 147	8
+ 587	4
+ 498	0
+ 167	6
+ 236	2
+ 785	7
+ 230	2
+ 904	1
+ 801	10
+ 405	10
+ 458	6
+ 515	5
+ 623	2
+ 810	7
+ 67	0
+ 486	2
+ 817	1
+ 619	3
+ 102	8
+ 926	3
+ 11	7
+ 998	2
+ 950	9
+ 297	8
+ 899	7
+ 743	4
+ 261	3
+ 871	9
+ 498	7
+ 585	6
+ 728	1
+ 779	5
+ 144	4
+ 861	2
+ 183	8
+ 585	2
+ 498	6
+ 436	4
+ 485	7
+ 200	4
+ 434	9
+ 741	7
+ 202	6
+ 578	7
+ 293	2
+ 264	0
+ 234	0
+ 566	4
+ 440	4
+ 624	6
+ 213	2
+ 817	7
+ 791	3
+ 160	3
+ 984	4
+ 660	4
+ 303	4
+ 113	5
+ 13	7
+ 204	3
+ 855	5
+ 326	1
+ 511	9
+ 467	10
+ 318	1
+ 573	5
+ 300	4
+ 242	1
+ 642	4
+ 367	6
+ 762	0
+ 45	1
+ 428	2
+ 570	4
+ 851	8
+ 746	7
+ 243	1
+ 795	8
+ 963	3
+ 705	3
+ 354	3
+ 811	7
+ 668	1
+ 744	3
+ 456	1
+ 937	2
+ 137	10
+ 283	6
+ 140	9
+ 5	10
+ 628	8
+ 697	9
+ 823	5
+ 626	8
+ 755	3
+ 66	1
+ 609	9
+ 762	3
+ 931	5
+ 586	4
+ 616	5
+ 604	8
+ 505	9
+ 318	6
+ 741	3
+ 636	4
+ 74	3
+ 241	9
+ 825	9
+ 683	6
+ 197	7
+ 688	8
+ 626	5
+ 82	6
+ 956	7
+ 944	6
+ 192	5
+ 325	7
+ 436	6
+ 342	2
+ 965	10
+ 547	0
+ 312	8
+ 936	1
+ 654	6
+ 717	9
+ 368	4
+ 658	10
+ 855	7
+ 551	8
+ 409	5
+ 382	6
+ 44	7
+ 298	5
+ 349	6
+ 658	3
+ 619	2
+ 354	9
+ 992	3
+ 67	6
+ 909	8
+ 498	3
+ 188	2
+ 271	0
+ 895	8
+ 854	3
+ 318	2
+ 905	4
+ 943	2
+ 843	3
+ 843	5
+ 607	5
+ 705	10
+ 392	7
+ 251	5
+ 343	2
+ 242	8
+ 437	4
+ 995	7
+ 474	9
+ 530	3
+ 195	8
+ 565	1
+ 210	5
+ 303	1
+ 800	1
+ 553	4
+ 609	3
+ 368	0
+ 955	6
+ 460	3
+ 780	7
+ 138	2
+ 133	1
+ 926	6
+ 24	5
+ 935	2
+ 304	5
+ 318	5
+ 8	6
+ 568	8
+ 768	1
+ 216	4
+ 379	6
+ 377	3
+ 204	8
+ 631	10
+ 539	8
+ 202	7
+ 901	1
+ 279	9
+ 584	2
+ 143	9
+ 714	5
+ 403	7
+ 83	10
+ 530	9
+ 92	7
+ 228	5
+ 331	6
+ 804	5
+ 442	4
+ 520	10
+ 203	7
+ 652	1
+ 850	9
+ 29	4
+ 145	2
+ 323	9
+ 633	7
+ 581	7
+ 696	1
+ 567	8
+ 857	8
+ 259	2
+ 400	1
+ 723	8
+ 498	2
+ 822	7
+ 965	5
+ 804	8
+ 405	8
+ 249	6
+ 5	6
+ 409	6
+ 297	10
+ 354	10
+ 100	9
+ 782	10
+ 716	0
+ 146	1
+ 104	9
+ 958	6
+ 112	8
+ 302	1
+ 255	1
+ 892	7
+ 939	1
+ 211	9
+ 713	6
+ 582	0
+ 609	9
+ 4	7
+ 857	8
+ 667	6
+ 828	8
+ 690	9
+ 683	6
+ 533	8
+ 428	8
+ 873	7
+ 942	8
+ 344	9
+ 907	6
+ 825	6
+ 174	4
+ 630	8
+ 343	6
+ 492	2
+ 421	2
+ 774	2
+ 972	5
+ 180	7
+ 112	7
+ 450	5
+ 549	3
+ 224	5
+ 88	6
+ 372	10
+ 122	2
+ 614	3
+ 604	2
+ 77	9
+ 880	6
+ 148	3
+ 728	9
+ 550	7
+ 386	7
+ 354	5
+ 444	8
+ 38	10
+ 127	3
+ 484	2
+ 829	9
+ 209	10
+ 53	8
+ 245	7
+ 68	3
+ 605	9
+ 892	8
+ 249	6
+ 674	8
+ 319	1
+ 529	7
+ 558	10
+ 478	6
+ 967	6
+ 858	5
+ 820	7
+ 307	0
+ 637	4
+ 852	9
+ 19	9
+ 205	6
+ 869	1
+ 376	1
+ 717	1
+ 917	0
+ 111	4
+ 709	7
+ 420	2
+ 265	4
+ 792	1
+ 838	6
+ 809	1
+ 640	4
+ 506	5
+ 328	5
+ 414	5
+ 148	3
+ 630	5
+ 400	3
+ 576	3
+ 382	7
+ 764	1
+ 356	2
+ 279	6
+ 571	1
+ 743	4
+ 683	6
+ 554	3
+ 998	1
+ 816	3
+ 585	2
+ 858	7
+ 512	5
+ 258	9
+ 835	8
+ 230	2
+ 520	10
+ 308	9
+ 177	6
+ 497	7
+ 659	2
+ 157	3
+ 793	7
+ 665	8
+ 772	5
+ 116	4
+ 711	10
+ 90	2
+ 463	3
+ 136	3
+ 181	4
+ 514	7
+ 359	8
+ 577	5
+ 410	1
+ 285	1
+ 314	4
+ 411	1
+ 153	1
+ 897	9
+ 557	0
+ 281	3
+ 988	4
+ 492	5
+ 719	6
+ 748	9
+ 993	3
+ 601	4
+ 85	2
+ 889	5
+ 251	2
+ 564	6
+ 616	10
+ 672	8
+ 50	6
+ 694	6
+ 582	10
+ 875	6
+ 346	4
+ 21	1
+ 994	8
+ 964	10
+ 31	6
+ 340	1
+ 742	2
+ 610	10
+ 402	2
+ 559	0
+ 148	2
+ 786	2
+ 800	5
+ 805	4
+ 455	7
+ 952	8
+ 48	10
+ 866	0
+ 741	8
+ 29	8
+ 395	4
+ 887	1
+ 597	5
+ 132	10
+ 670	7
+ 17	8
+ 921	8
+ 17	7
+ 283	8
+ 103	7
+ 503	1
+ 541	6
+ 27	4
+ 592	8
+ 238	6
+ 539	6
+ 990	4
+ 771	6
+ 922	9
+ 586	6
+ 593	6
+ 411	5
+ 406	4
+ 235	7
+ 250	3
+ 428	8
+ 393	10
+ 303	4
+ 376	9
+ 188	6
+ 516	7
+ 246	5
+ 153	0
+ 93	1
+ 919	7
+ 667	5
+ 282	1
+ 27	7
+ 506	3
+ 378	8
+ 600	8
+ 509	10
+ 775	8
+ 414	2
+ 707	6
+ 765	2
+ 328	0
+ 729	5
+ 28	8
+ 556	9
+ 501	2
+ 460	8
+ 301	5
+ 471	8
+ 749	8
+ 563	3
+ 655	1
+ 342	4
+ 883	8
+ 582	6
+ 357	3
+ 813	7
+ 357	5
+ 166	4
+ 364	7
+ 333	9
+ 945	8
+ 649	2
+ 280	1
+ 53	0
+ 969	6
+ 377	6
+ 688	7
+ 55	6
+ 476	6
+ 161	8
+ 983	10
+ 519	3
+ 516	7
+ 726	9
+ 407	1
+ 745	4
+ 853	4
+ 598	1
+ 514	7
+ 161	5
+ 268	5
+ 107	10
+ 258	2
+ 527	7
+ 799	7
+ 567	8
+ 663	1
+ 123	2
+ 772	8
+ 59	2
+ 909	8
+ 532	8
+ 197	1
+ 894	7
+ 781	1
+ 193	0
+ 593	3
+ 5	9
+ 463	5
+ 585	3
+ 221	2
+ 45	9
+ 238	2
+ 63	0
+ 18	1
+ 189	9
+ 925	7
+ 688	1
+ 851	6
+ 833	6
+ 636	0
+ 681	2
+ 327	7
+ 80	8
+ 217	7
+ 53	4
+ 817	1
+ 322	1
+ 266	4
+ 66	3
+ 506	3
+ 210	4
+ 976	9
+ 554	8
+ 480	4
+ 458	1
+ 414	1
+ 345	7
+ 824	4
+ 532	0
+ 90	6
+ 480	9
+ 683	8
+ 963	9
+ 187	0
+ 234	7
+ 284	4
+ 124	3
+ 342	7
+ 87	8
+ 66	5
+ 938	5
+ 683	3
+ 221	5
+ 708	8
+ 548	8
+ 338	0
+ 706	0
+ 830	7
+ 971	0
+ 699	2
+ 709	10
+ 649	8
+ 244	10
+ 511	3
+ 813	6
+ 875	8
+ 57	6
+ 34	3
+ 66	7
+ 30	6
+ 541	4
+ 642	2
+ 390	5
+ 917	4
+ 487	6
+ 565	2
+ 600	2
+ 29	8
+ 205	5
+ 174	0
+ 118	0
+ 769	2
+ 608	8
+ 452	7
+ 545	5
+ 288	1
+ 851	9
+ 333	2
+ 401	3
+ 601	9
+ 867	2
+ 84	5
+ 380	1
+ 311	6
+ 654	5
+ 604	8
+ 535	4
+ 947	1
+ 176	4
+ 817	7
+ 881	1
+ 808	7
+ 34	1
+ 972	4
+ 392	6
+ 322	3
+ 740	4
+ 725	1
+ 520	0
+ 706	2
+ 521	3
+ 947	1
+ 683	9
+ 199	9
+ 292	0
+ 581	2
+ 120	4
+ 905	2
+ 529	9
+ 588	9
+ 450	9
+ 179	2
+ 318	9
+ 310	8
+ 941	0
+ 13	5
+ 325	10
+ 518	0
+ 853	7
+ 867	1
+ 732	4
+ 318	9
+ 836	2
+ 6	4
+ 100	6
+ 287	6
+ 505	5
+ 741	8
+ 370	1
+ 661	3
+ 67	7
+ 773	4
+ 633	3
+ 401	5
+ 7	3
+ 631	7
+ 716	9
+ 592	6
+ 173	6
+ 917	3
+ 192	2
+ 824	7
+ 670	6
+ 520	0
+ 616	2
+ 351	7
+ 854	1
+ 75	5
+ 414	5
+ 973	4
+ 744	6
+ 160	5
+ 554	8
+ 11	7
+ 349	9
+ 0	5
+ 132	8
+ 239	8
+ 389	8
+ 842	0
+ 941	2
+ 688	8
+ 317	8
+ 282	7
+ 239	3
+ 151	10
+ 860	3
+ 441	4
+ 62	5
+ 141	4
+ 381	1
+ 952	5
+ 965	2
+ 315	4
+ 950	2
+ 359	9
+ 351	0
+ 686	7
+ 809	10
+ 397	0
+ 224	5
+ 30	1
+ 859	5
+ 497	9
+ 924	6
+ 331	3
+ 779	3
+ 818	7
+ 474	1
+ 99	4
+ 291	5
+ 316	6
+ 504	0
+ 308	3
+ 970	7
+ 361	2
+ 254	4
+ 277	1
+ 863	8
+ 33	8
+ 413	4
+ 93	2
+ 648	9
+ 937	1
+ 44	0
+ 546	3
+ 493	9
+ 976	10
+ 863	3
+ 310	8
+ 991	7
+ 27	2
+ 63	3
+ 358	9
+ 78	4
+ 714	5
+ 755	8
+ 682	4
+ 717	6
+ 525	8
+ 654	1
+ 97	1
+ 933	1
+ 144	8
+ 358	5
+ 629	3
+ 126	7
+ 593	2
+ 959	10
+ 115	0
+ 342	8
+ 527	1
+ 635	2
+ 501	4
+ 828	0
+ 114	5
+ 96	2
+ 630	0
+ 284	8
+ 825	6
+ 228	5
+ 990	4
+ 109	6
+ 542	1
+ 534	7
+ 105	9
+ 485	6
+ 974	1
+ 842	5
+ 473	7
+ 500	6
+ 152	6
+ 798	8
+ 625	1
+ 555	4
+ 723	8
+ 903	7
+ 136	0
+ 296	7
+ 80	8
+ 334	2
+ 706	8
+ 818	7
+ 940	7
+ 154	4
+ 329	7
+ 1000	5
+ 249	8
+ 264	9
+ 879	8
+ 323	6
+ 602	2
+ 314	7
+ 239	6
+ 416	3
+ 439	7
+ 505	1
+ 569	3
+ 825	5
+ 982	10
+ 921	3
+ 633	9
+ 793	9
+ 718	1
+ 756	6
+ 877	1
+ 198	5
+ 306	5
+ 217	5
+ 121	6
+ 864	6
+ 382	4
+ 706	10
+ 691	5
+ 460	7
+ 511	4
+ 985	1
+ 302	8
+ 26	0
+ 835	8
+ 617	7
+ 862	8
+ 191	2
+ 326	4
+ 713	4
+ 41	6
+ 8	4
+ 946	7
+ 375	6
+ 245	8
+ 310	8
+ 216	3
+ 900	5
+ 73	9
+ 538	9
+ 708	2
+ 620	6
+ 970	8
+ 738	3
+ 219	5
+ 743	3
+ 28	8
+ 683	10
+ 465	1
+ 611	7
+ 893	9
+ 466	1
+ 215	4
+ 626	3
+ 291	2
+ 196	10
+ 319	8
+ 569	3
+ 627	3
+ 585	8
+ 758	3
+ 107	8
+ 80	8
+ 759	5
+ 848	4
+ 255	7
+ 291	7
+ 849	5
+ 87	5
+ 794	4
+ 640	10
+ 378	10
+ 807	9
+ 249	4
+ 253	8
+ 281	0
+ 162	4
+ 797	2
+ 177	6
+ 787	0
+ 926	0
+ 766	2
+ 763	6
+ 723	9
+ 92	5
+ 228	7
+ 507	6
+ 692	3
+ 552	9
+ 748	8
+ 775	0
+ 817	9
+ 417	6
+ 179	6
+ 170	10
+ 619	1
+ 7	4
+ 312	8
+ 1	0
+ 621	1
+ 552	8
+ 825	1
+ 455	5
+ 373	0
+ 457	1
+ 813	2
+ 151	6
+ 169	6
+ 243	3
+ 161	4
+ 314	8
+ 508	3
+ 166	8
+ 92	2
+ 856	7
+ 259	4
+ 561	1
+ 467	0
+ 600	8
+ 24	1
+ 961	8
+ 289	1
+ 467	5
+ 679	7
+ 806	8
+ 124	1
+ 621	6
+ 441	8
+ 453	5
+ 954	3
+ 245	2
+ 716	8
+ 297	2
+ 823	9
+ 22	8
+ 955	10
+ 684	2
+ 95	2
+ 702	8
+ 862	5
+ 615	10
+ 628	2
+ 617	1
+ 23	1
+ 602	10
+ 378	8
+ 189	1
+ 654	5
+ 276	5
+ 383	3
+ 323	3
+ 281	0
+ 582	4
+ 159	3
+ 151	0
+ 793	8
+ 6	4
+ 2	6
+ 491	0
+ 693	1
+ 2	1
+ 941	2
+ 164	6
+ 677	4
+ 71	1
+ 737	4
+ 398	0
+ 402	10
+ 395	6
+ 264	5
+ 581	1
+ 312	6
+ 477	3
+ 209	10
+ 340	9
+ 61	3
+ 972	0
+ 532	1
+ 596	2
+ 576	7
+ 269	3
+ 61	7
+ 331	5
+ 647	7
+ 23	9
+ 272	6
+ 967	6
+ 190	4
+ 899	4
+ 413	2
+ 300	5
+ 581	3
+ 475	1
+ 408	1
+ 323	10
+ 738	6
+ 297	8
+ 259	6
+ 262	9
+ 354	3
+ 817	6
+ 890	8
+ 211	1
+ 230	1
+ 479	6
+ 350	8
+ 116	9
+ 52	6
+ 44	5
+ 662	4
+ 443	4
+ 959	7
+ 200	2
+ 368	5
+ 124	7
+ 748	9
+ 349	6
+ 727	6
+ 718	10
+ 671	2
+ 599	0
+ 977	7
+ 952	0
+ 307	10
+ 488	10
+ 363	9
+ 369	3
+ 672	6
+ 540	0
+ 31	7
+ 763	8
+ 606	1
+ 417	3
+ 672	1
+ 289	3
+ 333	9
+ 364	3
+ 604	3
+ 339	9
+ 311	8
+ 879	7
+ 759	2
+ 996	4
+ 817	5
+ 471	8
+ 199	2
+ 627	8
+ 346	0
+ 138	0
+ 180	4
+ 362	5
+ 316	7
+ 823	9
+ 41	2
+ 830	4
+ 989	7
+ 26	7
+ 957	0
+ 179	8
+ 557	7
+ 622	8
+ 885	2
+ 562	2
+ 294	7
+ 250	5
+ 128	6
+ 987	4
+ 337	8
+ 363	4
+ 972	2
+ 730	10
+ 901	8
+ 710	9
+ 777	9
+ 632	3
+ 540	3
+ 91	4
+ 503	7
+ 656	8
+ 353	9
+ 271	5
+ 517	3
+ 924	9
+ 68	3
+ 232	0
+ 480	10
+ 2	4
+ 717	7
+ 240	5
+ 600	9
+ 829	1
+ 126	9
+ 564	6
+ 573	2
+ 426	9
+ 126	7
+ 406	6
+ 955	3
+ 498	0
+ 618	7
+ 62	1
+ 692	1
+ 481	4
+ 775	7
+ 904	4
+ 592	7
+ 515	7
+ 652	1
+ 347	2
+ 300	8
+ 150	4
+ 471	6
+ 69	4
+ 887	6
+ 448	5
+ 297	5
+ 605	10
+ 574	1
+ 398	3
+ 806	3
+ 725	4
+ 34	2
+ 116	7
+ 320	5
+ 911	6
+ 237	1
+ 46	7
+ 619	1
+ 133	5
+ 682	6
+ 12	10
+ 91	6
+ 967	7
+ 702	4
+ 14	5
+ 667	7
+ 905	7
+ 978	0
+ 387	3
+ 484	3
+ 918	7
+ 361	10
+ 429	10
+ 78	6
+ 485	8
+ 143	5
+ 738	2
+ 113	7
+ 899	8
+ 70	9
+ 322	7
+ 651	2
+ 438	6
+ 247	8
+ 927	7
+ 124	8
+ 453	5
+ 808	9
+ 464	9
+ 445	9
+ 646	6
+ 446	4
+ 822	6
+ 89	7
+ 374	2
+ 633	7
+ 897	3
+ 923	3
+ 913	2
+ 160	8
+ 902	3
+ 684	4
+ 768	5
+ 237	2
+ 378	7
+ 181	0
+ 270	6
+ 408	1
+ 187	5
+ 814	6
+ 657	4
+ 257	6
+ 731	2
+ 889	6
+ 350	0
+ 484	3
+ 333	2
+ 607	1
+ 661	8
+ 333	0
+ 527	5
+ 63	8
+ 142	5
+ 890	3
+ 968	7
+ 889	6
+ 151	1
+ 179	9
+ 325	1
+ 526	7
+ 116	0
+ 927	4
+ 178	5
+ 550	8
+ 379	9
+ 877	9
+ 398	9
+ 703	5
+ 410	6
+ 868	4
+ 297	8
+ 3	4
+ 903	2
+ 329	2
+ 250	9
+ 903	4
+ 865	8
+ 815	0
+ 366	4
+ 881	7
+ 248	8
+ 651	6
+ 698	4
+ 185	1
+ 947	1
+ 487	2
+ 810	5
+ 691	7
+ 672	0
+ 940	9
+ 875	8
+ 287	7
diff --git a/contrib/bloom/expected/bloom.out b/contrib/bloom/expected/bloom.out
new file mode 100644
index ...f7334fa
*** a/contrib/bloom/expected/bloom.out
--- b/contrib/bloom/expected/bloom.out
***************
*** 0 ****
--- 1,120 ----
+ CREATE EXTENSION bloom;
+ CREATE TABLE tst (
+ 	i	int4,
+ 	t	text
+ );
+ \copy tst from 'data/data'
+ CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+ SET enable_seqscan=on;
+ SET enable_bitmapscan=off;
+ SET enable_indexscan=off;
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16;
+                 QUERY PLAN                 
+ -------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: (i = 16)
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: (i = 16)
+ (5 rows)
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE t = '5';
+                 QUERY PLAN                 
+ -------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: (t = '5'::text)
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: (t = '5'::text)
+ (5 rows)
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+                         QUERY PLAN                        
+ ----------------------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: ((i = 16) AND (t = '5'::text))
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: ((i = 16) AND (t = '5'::text))
+ (5 rows)
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ VACUUM ANALYZE tst;
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ VACUUM FULL tst;
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ RESET enable_seqscan;
+ RESET enable_bitmapscan;
+ RESET enable_indexscan;
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
new file mode 100644
index ...e5c7a86
*** a/contrib/bloom/sql/bloom.sql
--- b/contrib/bloom/sql/bloom.sql
***************
*** 0 ****
--- 1,46 ----
+ CREATE EXTENSION bloom;
+ 
+ CREATE TABLE tst (
+ 	i	int4,
+ 	t	text
+ );
+ 
+ \copy tst from 'data/data'
+ 
+ CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+ 
+ SET enable_seqscan=on;
+ SET enable_bitmapscan=off;
+ SET enable_indexscan=off;
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16;
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE t = '5';
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ VACUUM ANALYZE tst;
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ VACUUM FULL tst;
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ RESET enable_seqscan;
+ RESET enable_bitmapscan;
+ RESET enable_indexscan;
diff --git a/doc/src/sgml/bloom.sgml b/doc/src/sgml/bloom.sgml
new file mode 100644
index ...c207e6d
*** a/doc/src/sgml/bloom.sgml
--- b/doc/src/sgml/bloom.sgml
***************
*** 0 ****
--- 1,218 ----
+ <!-- doc/src/sgml/bloom.sgml -->
+ 
+ <sect1 id="bloom" xreflabel="bloom">
+  <title>bloom</title>
+ 
+  <indexterm zone="bloom">
+   <primary>bloom</primary>
+  </indexterm>
+ 
+  <para>
+   <literal>bloom</> is a contrib which implements index access method.  It comes
+   as example of custom access methods and generic WAL records usage.  But it
+   is also useful itself.
+  </para>
+ 
+  <sect2>
+   <title>Introduction</title>
+ 
+   <para>
+    Implementation of
+    <ulink url="http://en.wikipedia.org/wiki/Bloom_filter">Bloom filter</ulink>
+    allows fast exclusion of non-candidate tuples.
+    Since signature is a lossy representation of all indexed attributes, 
+    search results should be rechecked using heap information. 
+    User can specify signature length (in uint16, default is 5) and the number of 
+    bits, which can be setted, per attribute (1 < colN < 2048).
+   </para>
+ 
+   <para>
+    This index is useful if table has many attributes and queries can include
+    their arbitary combinations.  Traditional <literal>btree</> index is faster
+    than bloom index, but it'd require too many indexes to support all possible 
+    queries, while one need only one bloom index.  Bloom index supports only 
+    equality comparison.  Since it's a signature file, not a tree, it always
+    should be readed fully, but sequentially, so index search performance is 
+    constant and doesn't depend on a query. 
+   </para>
+  </sect2>
+ 
+  <sect2>
+   <title>Parameters</title>
+ 
+   <para>
+    <literal>bloom</> indexes accept following parameters in <literal>WITH</>
+    clause.
+   </para>
+ 
+    <variablelist>
+    <varlistentry>
+     <term><literal>length</></term>
+     <listitem>
+      <para>
+       Length of signature in uint16 type values
+      </para>
+     </listitem>
+    </varlistentry>
+    </variablelist>
+    <variablelist>
+    <varlistentry>
+     <term><literal>col1 &mdash; col16</></term>
+     <listitem>
+      <para>
+       Number of bits for corresponding column
+      </para>
+     </listitem>
+    </varlistentry>
+    </variablelist>
+  </sect2>
+ 
+  <sect2>
+   <title>Examples</title>
+ 
+   <para>
+    Example of index definition is given below.
+   </para>
+ 
+ <programlisting>
+ CREATE INDEX bloomidx ON tbloom(i1,i2,i3) 
+        WITH (length=5, col1=2, col2=2, col3=4);
+ </programlisting>
+ 
+   <para>
+    Here, we create bloom index with signature length 80 bits and attributes
+    i1, i2  mapped to 2 bits, attribute i3 - to 4 bits.
+   </para>
+ 
+   <para>
+    Example of index definition and usage is given below.
+   </para>
+ 
+ <programlisting>
+ CREATE TABLE tbloom AS
+ SELECT
+     random()::int as i1,
+     random()::int as i2,
+     random()::int as i3,
+     random()::int as i4,
+     random()::int as i5,
+     random()::int as i6,
+     random()::int as i7,
+     random()::int as i8,
+     random()::int as i9,
+     random()::int as i10,
+     random()::int as i11,
+     random()::int as i12,
+     random()::int as i13
+ FROM
+     generate_series(1,1000);
+ CREATE INDEX bloomidx ON tbloom USING
+              bloom (i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12);
+ SELECT pg_relation_size('bloomidx');
+ CREATE index btree_idx ON tbloom(i1,i2,i3,i4,i5,i6,i7,i8,i9,i10,i11,i12);
+ SELECT pg_relation_size('btree_idx');
+ </programlisting>
+ 
+ <programlisting>
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                                    QUERY PLAN
+ -----------------------------------------------------------------------------------------------------------------
+  Bitmap Heap Scan on tbloom  (cost=1.50..5.52 rows=1 width=52) (actual time=0.057..0.057 rows=0 loops=1)
+    Recheck Cond: ((i2 = 20) AND (i10 = 15))
+    ->  Bitmap Index Scan on bloomidx  (cost=0.00..1.50 rows=1 width=0) (actual time=0.041..0.041 rows=9 loops=1)
+          Index Cond: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.081 ms
+ (5 rows)
+ </programlisting>
+ 
+   <para>
+    Seqscan is slow.
+   </para>
+ 
+ <programlisting>
+ =# SET enable_bitmapscan = off;
+ =# SET enable_indexscan = off;
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                             QUERY PLAN
+ --------------------------------------------------------------------------------------------------
+  Seq Scan on tbloom  (cost=0.00..25.00 rows=1 width=52) (actual time=0.162..0.162 rows=0 loops=1)
+    Filter: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.181 ms
+ (3 rows)
+ </programlisting>
+ 
+  <para>
+   Btree index will be not used for this query.
+  </para>
+ 
+ <programlisting>
+ =# DROP INDEX bloomidx;
+ =# CREATE INDEX btree_idx ON tbloom(i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12);
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                             QUERY PLAN
+ --------------------------------------------------------------------------------------------------
+  Seq Scan on tbloom (cost=0.00..25.00 rows=1 width=52) (actual time=0.210..0.210 rows=0 loops=1)
+    Filter: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.250 ms
+ (3 rows)
+ </programlisting>
+  </sect2>
+ 
+  <sect2>
+   <title>Opclass interface</title>
+ 
+   <para>
+    Bloom opclass interface is simple.  It requires 1 supporting function:
+    hash function for indexing datatype.  And it provides 1 search operator:
+    equality operator.  The example below shows <literal>opclass</> definition
+    for <literal>text</> datatype.
+   </para>
+ 
+ <programlisting>
+ CREATE OPERATOR CLASS text_ops
+ DEFAULT FOR TYPE text USING bloom AS
+     OPERATOR    1   =(text, text),
+     FUNCTION    1   hashtext(text);
+ </programlisting>
+  </sect2>
+ 
+  <sect2>
+   <title>Limitation</title>
+   <para>
+ 
+    <itemizedlist>
+     <listitem>
+      <para>
+       For now, only opclasses for <literal>int4</>, <literal>text</> comes
+       with contrib.  However, users may define more of them.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       Only <literal>=</literal> operator is supported for search now.  But it's
+       possible to add support of arrays with contains and intersection
+       operations in future.
+      </para>
+     </listitem>
+    </itemizedlist>
+   </para>
+  </sect2>
+ 
+  <sect2>
+   <title>Authors</title>
+ 
+   <para>
+    Teodor Sigaev <email>teodor@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+ 
+   <para>
+    Alexander Korotkov <email>a.korotkov@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+ 
+   <para>
+    Oleg Bartunov <email>obartunov@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+  </sect2>
+ 
+ </sect1>
diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
new file mode 100644
index 1b3d2d9..c38b2f9
*** a/doc/src/sgml/contrib.sgml
--- b/doc/src/sgml/contrib.sgml
*************** CREATE EXTENSION <replaceable>module_nam
*** 105,110 ****
--- 105,111 ----
   &adminpack;
   &auth-delay;
   &auto-explain;
+  &bloom;
   &btree-gin;
   &btree-gist;
   &chkpass;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
new file mode 100644
index a12fee7..4a93ec2
*** a/doc/src/sgml/filelist.sgml
--- b/doc/src/sgml/filelist.sgml
***************
*** 106,111 ****
--- 106,112 ----
  <!ENTITY adminpack       SYSTEM "adminpack.sgml">
  <!ENTITY auth-delay      SYSTEM "auth-delay.sgml">
  <!ENTITY auto-explain    SYSTEM "auto-explain.sgml">
+ <!ENTITY bloom           SYSTEM "bloom.sgml">
  <!ENTITY btree-gin       SYSTEM "btree-gin.sgml">
  <!ENTITY btree-gist      SYSTEM "btree-gist.sgml">
  <!ENTITY chkpass         SYSTEM "chkpass.sgml">
#46Teodor Sigaev
teodor@sigaev.ru
In reply to: Alexander Korotkov (#45)
Re: WIP: Access method extendability

Next version of patch is attached:
* Documentation for CREATE ACCESS METHOD command is added.
* Refactoring and comments for bloom contrib.

Cool work, nice to see.

Some comments
1 create-am.7.patch: function lookup_index_am_handler_func() why doesn't it emit
error in case of emtpy input? If it checks signature of function and
empty handler is not allowed then, seems, all checks about handler have to be
moved in lookup_index_am_handler_func().

2 create-am.7.patch: Comment near get_am_name() incorrectly mentions get_am_oid
function

3 create-am.7.patch: get_am_name(Oid amOid, char amtype). Seems, amtype argument
is overengineering. get_am_name() is used only in error messages and additional
check in it will do nothing or even confuse user.

4 create-am.7.patch: Inconsistentcy with psql help. As I can see other code,
it's forbidden to create access method without handler
postgres=# \h create access method
Command: CREATE ACCESS METHOD
Description: define a new access method
Syntax:
CREATE ACCESS METHOD name
TYPE INDEX
[ HANDLER handler_function | NO HANDLER ]

postgres=# create access method yyy type index no handler;
ERROR: syntax error at or near "no"
LINE 1: create access method yyy type index no handler;

5 create-am.7.patch: file src/bin/pg_dump/pg_dump.h. Extra comma near DO_POLICY:
*** 77,83 ****
DO_POST_DATA_BOUNDARY,
DO_EVENT_TRIGGER,
DO_REFRESH_MATVIEW,
! DO_POLICY
} DumpableObjectType;

   typedef struct _dumpableObject
--- 78,84 ----
     DO_POST_DATA_BOUNDARY,
     DO_EVENT_TRIGGER,
     DO_REFRESH_MATVIEW,
!   DO_POLICY,
   } DumpableObjectType;

6 generic-xlog.7.patch: writeDelta() Seems, even under USE_ASSERT_CHECKING
define checking diff by its applying is to expensive. May be, let we use here
GENERIC_WAL_DEBUG macros?

7 generic-xlog.7.patch: src/backend/access/transam/generic_xlog.c It's unclear
for me, what does MATCH_THRESHOLD do or intended for? Pls, add comments here.

8 generic-xlog.7.patch: src/backend/access/transam/generic_xlog.c. Again, it's
unclear for me, what is motivation of size of PageData.data?

9 generic-xlog.7.patch: GenericXLogRegister(), Seems, emitting an error if
caller tries to register buffer which is already registered isn't good idea. In
practice, say, SP-GIST, buffer variable is used and page could be easily get
from buffer. Suppose, GenericXLogRegister() should not emit an error and ignore
isnew flag if case of second registration of the same buffer.

10 bloom-contrib.7.patch:
blutils.c: In function 'initBloomState':
blutils.c:128:20: warning: variable 'opaque' set but not used
[-Wunused-but-set-variable]
BloomPageOpaque opaque;

11 I'd really like to see regression tests (TAP-tests?) for replication with
generic xlog.

--
Teodor Sigaev E-mail: teodor@sigaev.ru
WWW: http://www.sigaev.ru/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#47Michael Paquier
michael.paquier@gmail.com
In reply to: Teodor Sigaev (#46)
Re: WIP: Access method extendability

On Wed, Feb 17, 2016 at 11:56 PM, Teodor Sigaev <teodor@sigaev.ru> wrote:

11 I'd really like to see regression tests (TAP-tests?) for replication with
generic xlog.

The recovery test suite can help to accomplish that.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#48Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Teodor Sigaev (#46)
3 attachment(s)
Re: WIP: Access method extendability

On Wed, Feb 17, 2016 at 5:56 PM, Teodor Sigaev <teodor@sigaev.ru> wrote:

Next version of patch is attached:

* Documentation for CREATE ACCESS METHOD command is added.
* Refactoring and comments for bloom contrib.

Cool work, nice to see.

Some comments
1 create-am.7.patch: function lookup_index_am_handler_func() why doesn't
it emit error in case of emtpy input? If it checks signature of function and
empty handler is not allowed then, seems, all checks about handler have to
be moved in lookup_index_am_handler_func().

Fixed. Now it's assumed that lookup_index_am_handler_func() returns valid
function Oid.

2 create-am.7.patch: Comment near get_am_name() incorrectly mentions

get_am_oid function

Fixed.

3 create-am.7.patch: get_am_name(Oid amOid, char amtype). Seems, amtype
argument is overengineering. get_am_name() is used only in error messages
and additional check in it will do nothing or even confuse user.

Fixed.

4 create-am.7.patch: Inconsistentcy with psql help. As I can see other
code, it's forbidden to create access method without handler
postgres=# \h create access method
Command: CREATE ACCESS METHOD
Description: define a new access method
Syntax:
CREATE ACCESS METHOD name
TYPE INDEX
[ HANDLER handler_function | NO HANDLER ]

postgres=# create access method yyy type index no handler;
ERROR: syntax error at or near "no"
LINE 1: create access method yyy type index no handler;

It comes from inconsistency in docs. Fixed.

5 create-am.7.patch: file src/bin/pg_dump/pg_dump.h. Extra comma near
DO_POLICY:
*** 77,83 ****
DO_POST_DATA_BOUNDARY,
DO_EVENT_TRIGGER,
DO_REFRESH_MATVIEW,
! DO_POLICY
} DumpableObjectType;

typedef struct _dumpableObject
--- 78,84 ----
DO_POST_DATA_BOUNDARY,
DO_EVENT_TRIGGER,
DO_REFRESH_MATVIEW,
!   DO_POLICY,
} DumpableObjectType;

Fixed.

6 generic-xlog.7.patch: writeDelta() Seems, even under USE_ASSERT_CHECKING
define checking diff by its applying is to expensive. May be, let we use
here GENERIC_WAL_DEBUG macros?

I decided not to introduce special macros for this. Now, this check is
enabled on WAL_DEBUG.

7 generic-xlog.7.patch: src/backend/access/transam/generic_xlog.c It's

unclear for me, what does MATCH_THRESHOLD do or intended for? Pls, add
comments here.

Explicit comments are added.

8 generic-xlog.7.patch: src/backend/access/transam/generic_xlog.c. Again,

it's unclear for me, what is motivation of size of PageData.data?

Explicit comments are added.

9 generic-xlog.7.patch: GenericXLogRegister(), Seems, emitting an error if
caller tries to register buffer which is already registered isn't good
idea. In practice, say, SP-GIST, buffer variable is used and page could be
easily get from buffer. Suppose, GenericXLogRegister() should not emit an
error and ignore isnew flag if case of second registration of the same
buffer.

Changed.

10 bloom-contrib.7.patch:
blutils.c: In function 'initBloomState':
blutils.c:128:20: warning: variable 'opaque' set but not used
[-Wunused-but-set-variable]
BloomPageOpaque opaque;

Fixed.

11 I'd really like to see regression tests (TAP-tests?) for replication
with generic xlog.

TAP test for replication added to bloom contrib. This test run on
additional make target wal-check.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

Attachments:

create-am.8.patchapplication/octet-stream; name=create-am.8.patchDownload
diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml
new file mode 100644
index 5f7befb..e5f8f1c
*** a/doc/src/sgml/indexam.sgml
--- b/doc/src/sgml/indexam.sgml
***************
*** 58,63 ****
--- 58,69 ----
    </para>
  
    <para>
+    Index access access methods can be defined and dropped using
+    <xref linkend="sql-createaccessmethod"> and
+     <xref linkend="sql-dropaccessmethod"> SQL commands respectively.
+   </para>
+ 
+   <para>
     An index access method handler function must be declared to accept a
     single argument of type <type>internal</> and to return the
     pseudo-type <type>index_am_handler</>.  The argument is a dummy value that
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
new file mode 100644
index bf95453..77667bd
*** a/doc/src/sgml/ref/allfiles.sgml
--- b/doc/src/sgml/ref/allfiles.sgml
*************** Complete list of usable sgml source file
*** 52,57 ****
--- 52,58 ----
  <!ENTITY commit             SYSTEM "commit.sgml">
  <!ENTITY commitPrepared     SYSTEM "commit_prepared.sgml">
  <!ENTITY copyTable          SYSTEM "copy.sgml">
+ <!ENTITY createAccessMethod SYSTEM "create_access_method.sgml">
  <!ENTITY createAggregate    SYSTEM "create_aggregate.sgml">
  <!ENTITY createCast         SYSTEM "create_cast.sgml">
  <!ENTITY createCollation    SYSTEM "create_collation.sgml">
*************** Complete list of usable sgml source file
*** 94,99 ****
--- 95,101 ----
  <!ENTITY delete             SYSTEM "delete.sgml">
  <!ENTITY discard            SYSTEM "discard.sgml">
  <!ENTITY do                 SYSTEM "do.sgml">
+ <!ENTITY dropAccessMethod   SYSTEM "drop_access_method.sgml">
  <!ENTITY dropAggregate      SYSTEM "drop_aggregate.sgml">
  <!ENTITY dropCast           SYSTEM "drop_cast.sgml">
  <!ENTITY dropCollation      SYSTEM "drop_collation.sgml">
diff --git a/doc/src/sgml/ref/create_access_method.sgml b/doc/src/sgml/ref/create_access_method.sgml
new file mode 100644
index ...f255ecc
*** a/doc/src/sgml/ref/create_access_method.sgml
--- b/doc/src/sgml/ref/create_access_method.sgml
***************
*** 0 ****
--- 1,120 ----
+ <!--
+ doc/src/sgml/ref/create_access_method.sgml
+ PostgreSQL documentation
+ -->
+ 
+ <refentry id="SQL-CREATEACCESSMETHOD">
+  <indexterm zone="sql-createaccessmethod">
+   <primary>CREATE ACCESS METHOD</primary>
+  </indexterm>
+ 
+  <refmeta>
+   <refentrytitle>CREATE ACCESS METHOD</refentrytitle>
+   <manvolnum>7</manvolnum>
+   <refmiscinfo>SQL - Language Statements</refmiscinfo>
+  </refmeta>
+ 
+  <refnamediv>
+   <refname>CREATE ACCESS METHOD</refname>
+   <refpurpose>define a new access method</refpurpose>
+  </refnamediv>
+ 
+  <refsynopsisdiv>
+ <synopsis>
+ CREATE ACCESS METHOD <replaceable class="parameter">name</replaceable>
+     TYPE INDEX
+     HANDLER <replaceable class="parameter">handler_function</replaceable>
+ </synopsis>
+  </refsynopsisdiv>
+ 
+  <refsect1>
+   <title>Description</title>
+ 
+   <para>
+    <command>CREATE ACCESS METHOD</command> creates a new access method.
+   </para>
+ 
+   <para>
+    The access method name must be unique within the database.
+   </para>
+ 
+   <para>
+    Only superusers can define new access methods.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Parameters</title>
+ 
+   <variablelist>
+    <varlistentry>
+     <term><replaceable class="parameter">name</replaceable></term>
+     <listitem>
+      <para>
+       The name of the access method to be created.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>TYPE INDEX</literal></term>
+     <listitem>
+      <para>
+       This clause specifies type of access method to define.
+       For now, there are only index access methods.  But intentionally
+       there would be other types of access methods.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>HANDLER <replaceable class="parameter">handler_function</replaceable></literal></term>
+     <listitem>
+      <para><replaceable class="parameter">handler_function</replaceable> is the
+       name of a previously registered function that will be called to
+       retrieve the struct which contains required parameters and functions
+       of access method to the core.  The handler function must take single
+       argument of type <type>internal</>, and its return type must be
+       <type>index_am_handler</type>.
+      </para>
+ 
+      <para>
+       See <xref linkend="index-api"> for index access methods API.
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Examples</title>
+ 
+   <para>
+    Create an access method <literal>bloom</> with
+    handler function <literal>blhandler</>:
+ <programlisting>
+ CREATE ACCESS METHOD bloom TYPE INDEX HANDLER blhandler;
+ </programlisting>
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Compatibility</title>
+ 
+   <para>
+    <command>CREATE ACCESS METHOD</command> is a
+    <productname>PostgreSQL</> extension.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>See Also</title>
+ 
+   <simplelist type="inline">
+    <member><xref linkend="sql-dropaccessmethod"></member>
+    <member><xref linkend="sql-createopclass"></member>
+    <member><xref linkend="sql-createopfamily"></member>
+   </simplelist>
+  </refsect1>
+ 
+ </refentry>
diff --git a/doc/src/sgml/ref/drop_access_method.sgml b/doc/src/sgml/ref/drop_access_method.sgml
new file mode 100644
index ...354b923
*** a/doc/src/sgml/ref/drop_access_method.sgml
--- b/doc/src/sgml/ref/drop_access_method.sgml
***************
*** 0 ****
--- 1,112 ----
+ <!--
+ doc/src/sgml/ref/drop_access_method.sgml
+ PostgreSQL documentation
+ -->
+ 
+ <refentry id="SQL-DROPACCESSMETHOD">
+  <indexterm zone="sql-dropaccessmethod">
+   <primary>DROP ACCESS METHOD</primary>
+  </indexterm>
+ 
+  <refmeta>
+   <refentrytitle>DROP ACCESS METHOD</refentrytitle>
+   <manvolnum>7</manvolnum>
+   <refmiscinfo>SQL - Language Statements</refmiscinfo>
+  </refmeta>
+ 
+  <refnamediv>
+   <refname>DROP ACCESS METHOD</refname>
+   <refpurpose>remove an access method</refpurpose>
+  </refnamediv>
+ 
+  <refsynopsisdiv>
+ <synopsis>
+ DROP ACCESS METHOD [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ CASCADE | RESTRICT ]
+ </synopsis>
+  </refsynopsisdiv>
+ 
+  <refsect1>
+   <title>Description</title>
+ 
+   <para>
+    <command>DROP ACCESS METHOD</command> removes an existing access method.
+    Only superusers can drop access methods.
+   </para>
+ 
+   <para>
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Parameters</title>
+ 
+   <variablelist>
+    <varlistentry>
+     <term><literal>IF EXISTS</literal></term>
+     <listitem>
+      <para>
+       Do not throw an error if the access method does not exist.
+       A notice is issued in this case.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><replaceable class="parameter">name</replaceable></term>
+     <listitem>
+      <para>
+       The name of an existing access method.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>CASCADE</literal></term>
+     <listitem>
+      <para>
+       Automatically drop objects that depend on the access method
+       (such as operator classes, operator families, indexes).
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>RESTRICT</literal></term>
+     <listitem>
+      <para>
+       Refuse to drop the access method if any objects depend on it.
+       This is the default.
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Examples</title>
+ 
+   <para>
+    Drop the access method <literal>bloom</>:
+ <programlisting>
+ DROP ACCESS METHOD bloom;
+ </programlisting></para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Compatibility</title>
+ 
+   <para>
+    <command>DROP ACCESS METHOD</command> is a
+    <productname>PostgreSQL</> extension.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>See Also</title>
+ 
+   <simplelist type="inline">
+    <member><xref linkend="sql-createaccessmethod"></member>
+   </simplelist>
+  </refsect1>
+ 
+ </refentry>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
new file mode 100644
index 03020df..8acdff1
*** a/doc/src/sgml/reference.sgml
--- b/doc/src/sgml/reference.sgml
***************
*** 80,85 ****
--- 80,86 ----
     &commit;
     &commitPrepared;
     &copyTable;
+    &createAccessMethod;
     &createAggregate;
     &createCast;
     &createCollation;
***************
*** 122,127 ****
--- 123,129 ----
     &delete;
     &discard;
     &do;
+    &dropAccessMethod;
     &dropAggregate;
     &dropCast;
     &dropCollation;
diff --git a/src/backend/access/index/amapi.c b/src/backend/access/index/amapi.c
new file mode 100644
index bda166a..053d3bb
*** a/src/backend/access/index/amapi.c
--- b/src/backend/access/index/amapi.c
*************** GetIndexAmRoutineByAmId(Oid amoid)
*** 62,67 ****
--- 62,74 ----
  			 amoid);
  	amform = (Form_pg_am) GETSTRUCT(tuple);
  
+ 	/* Check if it's index access method */
+ 	if (amform->amtype != 'i')
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("access method \"%s\" type is not index",
+ 						NameStr(amform->amname))));
+ 
  	amhandler = amform->amhandler;
  
  	/* Complain if handler OID is invalid */
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
new file mode 100644
index c48e37b..a86a488
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 20,25 ****
--- 20,26 ----
  #include "catalog/heap.h"
  #include "catalog/index.h"
  #include "catalog/objectaccess.h"
+ #include "catalog/pg_am.h"
  #include "catalog/pg_amop.h"
  #include "catalog/pg_amproc.h"
  #include "catalog/pg_attrdef.h"
*************** static const Oid object_classes[] = {
*** 160,166 ****
  	ExtensionRelationId,		/* OCLASS_EXTENSION */
  	EventTriggerRelationId,		/* OCLASS_EVENT_TRIGGER */
  	PolicyRelationId,			/* OCLASS_POLICY */
! 	TransformRelationId			/* OCLASS_TRANSFORM */
  };
  
  
--- 161,168 ----
  	ExtensionRelationId,		/* OCLASS_EXTENSION */
  	EventTriggerRelationId,		/* OCLASS_EVENT_TRIGGER */
  	PolicyRelationId,			/* OCLASS_POLICY */
! 	TransformRelationId,		/* OCLASS_TRANSFORM */
! 	AccessMethodRelationId		/* OCLASS_AM */
  };
  
  
*************** doDeletion(const ObjectAddress *object, 
*** 1270,1275 ****
--- 1272,1280 ----
  
  		case OCLASS_TRANSFORM:
  			DropTransformById(object->objectId);
+ 
+ 		case OCLASS_AM:
+ 			RemoveAccessMethodById(object->objectId);
  			break;
  
  		default:
*************** getObjectClass(const ObjectAddress *obje
*** 2415,2420 ****
--- 2420,2428 ----
  
  		case TransformRelationId:
  			return OCLASS_TRANSFORM;
+ 
+ 		case AccessMethodRelationId:
+ 			return OCLASS_AM;
  	}
  
  	/* shouldn't get here */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
new file mode 100644
index d2aaa6d..83e8b24
*** a/src/backend/catalog/objectaddress.c
--- b/src/backend/catalog/objectaddress.c
*************** static const ObjectPropertyType ObjectPr
*** 438,443 ****
--- 438,455 ----
  		Anum_pg_type_typacl,
  		ACL_KIND_TYPE,
  		true
+ 	},
+ 	{
+ 		AccessMethodRelationId,
+ 		AmOidIndexId,
+ 		AMOID,
+ 		AMNAME,
+ 		Anum_pg_am_amname,
+ 		InvalidAttrNumber,
+ 		InvalidAttrNumber,
+ 		InvalidAttrNumber,
+ 		-1,
+ 		true
  	}
  };
  
*************** static ObjectAddress get_object_address_
*** 674,679 ****
--- 686,693 ----
  							   List *objargs, bool missing_ok);
  static ObjectAddress get_object_address_defacl(List *objname, List *objargs,
  						  bool missing_ok);
+ static ObjectAddress get_object_address_am(ObjectType objtype, List *objname,
+ 						bool missing_ok);
  static const ObjectPropertyType *get_object_property_data(Oid class_id);
  
  static void getRelationDescription(StringInfo buffer, Oid relid);
*************** get_object_address(ObjectType objtype, L
*** 913,918 ****
--- 927,935 ----
  				address = get_object_address_defacl(objname, objargs,
  													missing_ok);
  				break;
+ 			case OBJECT_ACCESS_METHOD:
+ 				address = get_object_address_am(objtype, objname, missing_ok);
+ 				break;
  			default:
  				elog(ERROR, "unrecognized objtype: %d", (int) objtype);
  				/* placate compiler, in case it thinks elog might return */
*************** get_object_address_opcf(ObjectType objty
*** 1489,1495 ****
  	ObjectAddress address;
  
  	/* XXX no missing_ok support here */
! 	amoid = get_am_oid(strVal(linitial(objname)), false);
  	objname = list_copy_tail(objname, 1);
  
  	switch (objtype)
--- 1506,1512 ----
  	ObjectAddress address;
  
  	/* XXX no missing_ok support here */
! 	amoid = get_am_oid(strVal(linitial(objname)), 'i', false);
  	objname = list_copy_tail(objname, 1);
  
  	switch (objtype)
*************** get_object_address_opcf(ObjectType objty
*** 1516,1521 ****
--- 1533,1597 ----
  }
  
  /*
+  * Find the ObjectAddress for an access method.
+  */
+ static ObjectAddress
+ get_object_address_am(ObjectType objtype, List *objname, bool missing_ok)
+ {
+ 	ObjectAddress	address;
+ 	char		   *amname, *catalogname;
+ 	Type		tup;
+ 
+ 	switch (list_length(objname))
+ 	{
+ 		case 1:
+ 			amname = strVal(linitial(objname));
+ 			break;
+ 		case 2:
+ 			catalogname = strVal(linitial(objname));
+ 			amname = strVal(lsecond(objname));
+ 
+ 			/*
+ 			 * We check the catalog name and then ignore it.
+ 			 */
+ 			if (strcmp(catalogname, get_database_name(MyDatabaseId)) != 0)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				  errmsg("cross-database references are not implemented: %s",
+ 						 NameListToString(objname))));
+ 			break;
+ 		default:
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_SYNTAX_ERROR),
+ 				errmsg("improper access method name (too many dotted names): %s",
+ 					   NameListToString(objname))));
+ 			break;
+ 	}
+ 
+ 	address.classId = AccessMethodRelationId;
+ 	address.objectId = InvalidOid;
+ 	address.objectSubId = 0;
+ 
+ 	tup = SearchSysCache1(AMNAME, PointerGetDatum(amname));
+ 	if (!HeapTupleIsValid(tup))
+ 	{
+ 		/* Access method is missing, report error if needed */
+ 		if (!missing_ok)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 					 errmsg("access method \"%s\" does not exist",
+ 							amname)));
+ 		return address;
+ 	}
+ 
+ 	/* Access method is found, return its oid */
+ 	address.objectId = HeapTupleGetOid(tup);
+ 	ReleaseSysCache(tup);
+ 
+ 	return address;
+ }
+ 
+ /*
   * Find the ObjectAddress for an opclass/opfamily member.
   *
   * (The returned address corresponds to a pg_amop/pg_amproc object).
*************** check_object_ownership(Oid roleid, Objec
*** 2179,2184 ****
--- 2255,2261 ----
  			break;
  		case OBJECT_TSPARSER:
  		case OBJECT_TSTEMPLATE:
+ 		case OBJECT_ACCESS_METHOD:
  			/* We treat these object types as being owned by superusers */
  			if (!superuser_arg(roleid))
  				ereport(ERROR,
*************** getObjectDescription(const ObjectAddress
*** 3129,3134 ****
--- 3206,3226 ----
  				break;
  			}
  
+ 		case OCLASS_AM:
+ 			{
+ 				HeapTuple	tup;
+ 
+ 				tup = SearchSysCache1(AMOID,
+ 									  ObjectIdGetDatum(object->objectId));
+ 				if (!HeapTupleIsValid(tup))
+ 					elog(ERROR, "cache lookup failed for access method %u",
+ 						 object->objectId);
+ 				appendStringInfo(&buffer, _("access method %s"),
+ 							NameStr(((Form_pg_am) GETSTRUCT(tup))->amname));
+ 				ReleaseSysCache(tup);
+ 				break;
+ 			}
+ 
  		default:
  			appendStringInfo(&buffer, "unrecognized object %u %u %d",
  							 object->classId,
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
new file mode 100644
index b1ac704..6b3742c
*** a/src/backend/commands/Makefile
--- b/src/backend/commands/Makefile
*************** subdir = src/backend/commands
*** 12,18 ****
  top_builddir = ../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o  \
  	collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
  	dbcommands.o define.o discard.o dropcmds.o \
  	event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
--- 12,18 ----
  top_builddir = ../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
  	collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
  	dbcommands.o define.o discard.o dropcmds.o \
  	event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
new file mode 100644
index ...6c6996f
*** a/src/backend/commands/amcmds.c
--- b/src/backend/commands/amcmds.c
***************
*** 0 ****
--- 1,277 ----
+ /*-------------------------------------------------------------------------
+  *
+  * amcmds.c
+  *	  Routines for SQL commands that manipulate access methods.
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *
+  * IDENTIFICATION
+  *	  src/backend/commands/amcmds.c
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "access/heapam.h"
+ #include "access/htup_details.h"
+ #include "access/xact.h"
+ #include "catalog/binary_upgrade.h"
+ #include "catalog/catalog.h"
+ #include "catalog/dependency.h"
+ #include "catalog/heap.h"
+ #include "catalog/indexing.h"
+ #include "catalog/objectaccess.h"
+ #include "catalog/pg_authid.h"
+ #include "catalog/pg_am.h"
+ #include "catalog/pg_collation.h"
+ #include "catalog/pg_constraint.h"
+ #include "catalog/pg_depend.h"
+ #include "catalog/pg_enum.h"
+ #include "catalog/pg_language.h"
+ #include "catalog/pg_namespace.h"
+ #include "catalog/pg_proc.h"
+ #include "catalog/pg_proc_fn.h"
+ #include "catalog/pg_range.h"
+ #include "catalog/pg_type.h"
+ #include "catalog/pg_type_fn.h"
+ #include "commands/dbcommands.h"
+ #include "commands/defrem.h"
+ #include "commands/tablecmds.h"
+ #include "commands/typecmds.h"
+ #include "executor/executor.h"
+ #include "miscadmin.h"
+ #include "nodes/makefuncs.h"
+ #include "optimizer/planner.h"
+ #include "optimizer/var.h"
+ #include "parser/parse_coerce.h"
+ #include "parser/parse_collate.h"
+ #include "parser/parse_expr.h"
+ #include "parser/parse_func.h"
+ #include "parser/parse_type.h"
+ #include "utils/acl.h"
+ #include "utils/builtins.h"
+ #include "utils/fmgroids.h"
+ #include "utils/lsyscache.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ #include "utils/snapmgr.h"
+ #include "utils/syscache.h"
+ #include "utils/tqual.h"
+ 
+ /*
+  * Convert a handler function name passed from the parser to an Oid. This
+  * function either return valid function Oid or throw an error.
+  */
+ static Oid
+ lookup_index_am_handler_func(List *handler_name)
+ {
+ 	Oid			handlerOid;
+ 	Oid			funcargtypes[1] = {INTERNALOID};
+ 
+ 	if (handler_name == NIL)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
+ 				 errmsg("handler function is not specified")));
+ 
+ 	/* handlers have no arguments */
+ 	handlerOid = LookupFuncName(handler_name, 1, funcargtypes, false);
+ 
+ 	/* check that handler has correct return type */
+ 	if (get_func_rettype(handlerOid) != INDEX_AM_HANDLEROID)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 				 errmsg("function %s must return type \"index_am_handler\"",
+ 						NameListToString(handler_name))));
+ 
+ 	return handlerOid;
+ }
+ 
+ 
+ /*
+  * CreateAcessMethod
+  *		Registers a new access method.
+  */
+ ObjectAddress
+ CreateAccessMethod(CreateAmStmt *stmt)
+ {
+ 	Relation		rel;
+ 	ObjectAddress	myself;
+ 	ObjectAddress	referenced;
+ 	Oid				amoid;
+ 	Oid				amhandler;
+ 	bool			nulls[Natts_pg_am];
+ 	Datum			values[Natts_pg_am];
+ 	HeapTuple		tup;
+ 
+ 	rel = heap_open(AccessMethodRelationId, RowExclusiveLock);
+ 
+ 	/* Must be super user */
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 			errmsg("permission denied to create access method \"%s\"",
+ 				   stmt->amname),
+ 			errhint("Must be superuser to create access method.")));
+ 
+ 	/* Check if name is busy */
+ 	amoid = GetSysCacheOid1(AMNAME, CStringGetDatum(stmt->amname));
+ 	if (OidIsValid(amoid))
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_DUPLICATE_OBJECT),
+ 				 errmsg("access method \"%s\" already exists", stmt->amname)));
+ 	}
+ 
+ 	/*
+ 	 * Get handler function oid. Handler signature depends on access method
+ 	 * type.
+ 	 */
+ 	switch(stmt->amtype)
+ 	{
+ 		case 'i':
+ 			amhandler = lookup_index_am_handler_func(stmt->handler_name);
+ 			break;
+ 		default:
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 					 errmsg("wrong access method type \"%c\"", stmt->amtype)));
+ 			break;
+ 	}
+ 
+ 	/*
+ 	 * Insert tuple into pg_am.
+ 	 */
+ 	memset(values, 0, sizeof(values));
+ 	memset(nulls, false, sizeof(nulls));
+ 
+ 	values[Anum_pg_am_amname - 1] =
+ 		DirectFunctionCall1(namein, CStringGetDatum(stmt->amname));
+ 	values[Anum_pg_am_amhandler - 1] = ObjectIdGetDatum(amhandler);
+ 	values[Anum_pg_am_amtype - 1] = CharGetDatum(stmt->amtype);
+ 
+ 	tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+ 
+ 	amoid = simple_heap_insert(rel, tup);
+ 	CatalogUpdateIndexes(rel, tup);
+ 	heap_freetuple(tup);
+ 
+ 	myself.classId = AccessMethodRelationId;
+ 	myself.objectId = amoid;
+ 	myself.objectSubId = 0;
+ 
+ 	/* Record dependecy on handler function */
+ 	referenced.classId = ProcedureRelationId;
+ 	referenced.objectId = amhandler;
+ 	referenced.objectSubId = 0;
+ 
+ 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ 
+ 	recordDependencyOnCurrentExtension(&myself, false);
+ 
+ 	heap_close(rel, RowExclusiveLock);
+ 
+ 	return myself;
+ }
+ 
+ /*
+  * Guts of access method deletion.
+  */
+ void
+ RemoveAccessMethodById(Oid amOid)
+ {
+ 	Relation	relation;
+ 	HeapTuple	tup;
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 errmsg("must be superuser to drop access methods")));
+ 
+ 	relation = heap_open(AccessMethodRelationId, RowExclusiveLock);
+ 
+ 	tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
+ 	if (!HeapTupleIsValid(tup))
+ 		elog(ERROR, "cache lookup failed for access method %u", amOid);
+ 
+ 	simple_heap_delete(relation, &tup->t_self);
+ 
+ 	ReleaseSysCache(tup);
+ 
+ 	heap_close(relation, RowExclusiveLock);
+ }
+ 
+ /*
+  * Convert single charater access method type into string for error reporting.
+  */
+ static char *
+ get_am_type_string(char amtype)
+ {
+ 	switch (amtype)
+ 	{
+ 		case 'i':
+ 			return "index";
+ 		default:
+ 			elog(ERROR, "invalid access method type '%c'", amtype);
+ 	}
+ }
+ 
+ /*
+  * get_am_oid - given an access method name and type, look up the OID
+  *
+  * If missing_ok is false, throw an error if access method not found.  If
+  * true, just return InvalidOid.
+  */
+ Oid
+ get_am_oid(const char *amname, char amtype, bool missing_ok)
+ {
+ 	HeapTuple	tup;
+ 	Oid			oid = InvalidOid;
+ 
+ 	tup = SearchSysCache1(AMNAME, CStringGetDatum(amname));
+ 	if (HeapTupleIsValid(tup))
+ 	{
+ 		Form_pg_am amform = (Form_pg_am) GETSTRUCT(tup);
+ 
+ 		if (amform->amtype != amtype)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 					 errmsg("access method \"%s\" type is not %s",
+ 							NameStr(amform->amname),
+ 							get_am_type_string(amtype))));
+ 
+ 		oid = HeapTupleGetOid(tup);
+ 		ReleaseSysCache(tup);
+ 	}
+ 
+ 	if (!OidIsValid(oid) && !missing_ok)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 				 errmsg("access method \"%s\" does not exist", amname)));
+ 	return oid;
+ }
+ 
+ /*
+  * get_am_name - given an access method OID name and type, look up the name
+  *
+  * Access method type is not required for lookup.  However it's useful to check
+  * the type to ensure it is what we're looking for.
+  */
+ char *
+ get_am_name(Oid amOid)
+ {
+ 	HeapTuple	tup;
+ 	char	   *result = NULL;
+ 
+ 	tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
+ 	if (HeapTupleIsValid(tup))
+ 	{
+ 		Form_pg_am amform = (Form_pg_am) GETSTRUCT(tup);
+ 
+ 		result = pstrdup(NameStr(amform->amname));
+ 		ReleaseSysCache(tup);
+ 	}
+ 	return result;
+ }
+ 
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
new file mode 100644
index 9e32f8d..3f52ad8
*** a/src/backend/commands/event_trigger.c
--- b/src/backend/commands/event_trigger.c
*************** typedef enum
*** 86,91 ****
--- 86,92 ----
  
  /* XXX merge this with ObjectTypeMap? */
  static event_trigger_support_data event_trigger_support[] = {
+ 	{"ACCESS METHOD", true},
  	{"AGGREGATE", true},
  	{"CAST", true},
  	{"CONSTRAINT", true},
*************** EventTriggerSupportsObjectType(ObjectTyp
*** 1078,1083 ****
--- 1079,1085 ----
  		case OBJECT_EVENT_TRIGGER:
  			/* no support for event triggers on event triggers */
  			return false;
+ 		case OBJECT_ACCESS_METHOD:
  		case OBJECT_AGGREGATE:
  		case OBJECT_AMOP:
  		case OBJECT_AMPROC:
*************** EventTriggerSupportsObjectClass(ObjectCl
*** 1167,1172 ****
--- 1169,1175 ----
  		case OCLASS_DEFACL:
  		case OCLASS_EXTENSION:
  		case OCLASS_POLICY:
+ 		case OCLASS_AM:
  			return true;
  	}
  
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
new file mode 100644
index 8a66196..d8da8d1
*** a/src/backend/commands/opclasscmds.c
--- b/src/backend/commands/opclasscmds.c
*************** DefineOpClass(CreateOpClassStmt *stmt)
*** 678,683 ****
--- 678,689 ----
  	myself.objectId = opclassoid;
  	myself.objectSubId = 0;
  
+ 	/* dependency on access method */
+ 	referenced.classId = AccessMethodRelationId;
+ 	referenced.objectId = amoid;
+ 	referenced.objectSubId = 0;
+ 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ 
  	/* dependency on namespace */
  	referenced.classId = NamespaceRelationId;
  	referenced.objectId = namespaceoid;
*************** DefineOpFamily(CreateOpFamilyStmt *stmt)
*** 743,749 ****
  					   get_namespace_name(namespaceoid));
  
  	/* Get access method OID, throwing an error if it doesn't exist. */
! 	amoid = get_am_oid(stmt->amname, false);
  
  	/* XXX Should we make any privilege check against the AM? */
  
--- 749,755 ----
  					   get_namespace_name(namespaceoid));
  
  	/* Get access method OID, throwing an error if it doesn't exist. */
! 	amoid = get_am_oid(stmt->amname, 'i', false);
  
  	/* XXX Should we make any privilege check against the AM? */
  
*************** RemoveAmProcEntryById(Oid entryOid)
*** 1663,1683 ****
  	heap_close(rel, RowExclusiveLock);
  }
  
- char *
- get_am_name(Oid amOid)
- {
- 	HeapTuple	tup;
- 	char	   *result = NULL;
- 
- 	tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
- 	if (HeapTupleIsValid(tup))
- 	{
- 		result = pstrdup(NameStr(((Form_pg_am) GETSTRUCT(tup))->amname));
- 		ReleaseSysCache(tup);
- 	}
- 	return result;
- }
- 
  /*
   * Subroutine for ALTER OPERATOR CLASS SET SCHEMA/RENAME
   *
--- 1669,1674 ----
*************** IsThereOpFamilyInNamespace(const char *o
*** 1723,1744 ****
  						get_am_name(opfmethod),
  						get_namespace_name(opfnamespace))));
  }
- 
- /*
-  * get_am_oid - given an access method name, look up the OID
-  *
-  * If missing_ok is false, throw an error if access method not found.  If
-  * true, just return InvalidOid.
-  */
- Oid
- get_am_oid(const char *amname, bool missing_ok)
- {
- 	Oid			oid;
- 
- 	oid = GetSysCacheOid1(AMNAME, CStringGetDatum(amname));
- 	if (!OidIsValid(oid) && !missing_ok)
- 		ereport(ERROR,
- 				(errcode(ERRCODE_UNDEFINED_OBJECT),
- 				 errmsg("access method \"%s\" does not exist", amname)));
- 	return oid;
- }
--- 1714,1716 ----
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
new file mode 100644
index a9e9cc3..85bd5ac
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copyCreateTransformStmt(const CreateTra
*** 3828,3833 ****
--- 3828,3845 ----
  	return newnode;
  }
  
+ static CreateAmStmt *
+ _copyCreateAmStmt(const CreateAmStmt *from)
+ {
+ 	CreateAmStmt *newnode = makeNode(CreateAmStmt);
+ 
+ 	COPY_STRING_FIELD(amname);
+ 	COPY_NODE_FIELD(handler_name);
+ 	COPY_SCALAR_FIELD(amtype);
+ 
+ 	return newnode;
+ }
+ 
  static CreateTrigStmt *
  _copyCreateTrigStmt(const CreateTrigStmt *from)
  {
*************** copyObject(const void *from)
*** 4819,4824 ****
--- 4831,4839 ----
  		case T_CreateTransformStmt:
  			retval = _copyCreateTransformStmt(from);
  			break;
+ 		case T_CreateAmStmt:
+ 			retval = _copyCreateAmStmt(from);
+ 			break;
  		case T_CreateTrigStmt:
  			retval = _copyCreateTrigStmt(from);
  			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
new file mode 100644
index b9c3959..f19fde1
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
*************** _equalCreateTransformStmt(const CreateTr
*** 1855,1860 ****
--- 1855,1870 ----
  }
  
  static bool
+ _equalCreateAmStmt(const CreateAmStmt *a, const CreateAmStmt *b)
+ {
+ 	COMPARE_STRING_FIELD(amname);
+ 	COMPARE_NODE_FIELD(handler_name);
+ 	COMPARE_SCALAR_FIELD(amtype);
+ 
+ 	return true;
+ }
+ 
+ static bool
  _equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b)
  {
  	COMPARE_STRING_FIELD(trigname);
*************** equal(const void *a, const void *b)
*** 3146,3151 ****
--- 3156,3164 ----
  		case T_CreateTransformStmt:
  			retval = _equalCreateTransformStmt(a, b);
  			break;
+ 		case T_CreateAmStmt:
+ 			retval = _equalCreateAmStmt(a, b);
+ 			break;
  		case T_CreateTrigStmt:
  			retval = _equalCreateTrigStmt(a, b);
  			break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
new file mode 100644
index b307b48..fe95580
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
*************** static Node *makeRecursiveViewSelect(cha
*** 263,269 ****
  		DeallocateStmt PrepareStmt ExecuteStmt
  		DropOwnedStmt ReassignOwnedStmt
  		AlterTSConfigurationStmt AlterTSDictionaryStmt
! 		CreateMatViewStmt RefreshMatViewStmt
  
  %type <node>	select_no_parens select_with_parens select_clause
  				simple_select values_clause
--- 263,269 ----
  		DeallocateStmt PrepareStmt ExecuteStmt
  		DropOwnedStmt ReassignOwnedStmt
  		AlterTSConfigurationStmt AlterTSDictionaryStmt
! 		CreateMatViewStmt RefreshMatViewStmt CreateAmStmt DropAmStmt
  
  %type <node>	select_no_parens select_with_parens select_clause
  				simple_select values_clause
*************** static Node *makeRecursiveViewSelect(cha
*** 604,610 ****
  	LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
  	LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
  
! 	MAPPING MATCH MATERIALIZED MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
  
  	NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NONE
  	NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
--- 604,610 ----
  	LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
  	LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
  
! 	MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE
  
  	NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NONE
  	NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
*************** stmt :
*** 789,794 ****
--- 789,795 ----
  			| CommentStmt
  			| ConstraintsSetStmt
  			| CopyStmt
+ 			| CreateAmStmt
  			| CreateAsStmt
  			| CreateAssertStmt
  			| CreateCastStmt
*************** stmt :
*** 823,828 ****
--- 824,830 ----
  			| DeleteStmt
  			| DiscardStmt
  			| DoStmt
+ 			| DropAmStmt
  			| DropAssertStmt
  			| DropCastStmt
  			| DropFdwStmt
*************** row_security_cmd:
*** 4708,4713 ****
--- 4710,4764 ----
  
  /*****************************************************************************
   *
+  *		QUERY:
+  *             CREATE ACCESS METHOD name HANDLER handler_name
+  *
+  *****************************************************************************/
+ 
+ CreateAmStmt: CREATE ACCESS METHOD name TYPE_P INDEX HANDLER handler_name
+ 				{
+ 					CreateAmStmt *n = makeNode(CreateAmStmt);
+ 					n->amname = $4;
+ 					n->handler_name = $8;
+ 					n->amtype = 'i';
+ 					$$ = (Node *) n;
+ 				}
+ 		;
+ 
+ /*****************************************************************************
+  *
+  *		QUERY :
+  *				DROP ACCESS METHOD name
+  *
+  ****************************************************************************/
+ 
+ DropAmStmt: DROP ACCESS METHOD name opt_drop_behavior
+ 				{
+ 					DropStmt *n = makeNode(DropStmt);
+ 					n->removeType = OBJECT_ACCESS_METHOD;
+ 					n->objects = list_make1(list_make1(makeString($4)));
+ 					n->arguments = NIL;
+ 					n->missing_ok = false;
+ 					n->behavior = $5;
+ 					n->concurrent = false;
+ 					$$ = (Node *) n;
+ 				}
+ 				|  DROP ACCESS METHOD IF_P EXISTS name opt_drop_behavior
+ 				{
+ 					DropStmt *n = makeNode(DropStmt);
+ 					n->removeType = OBJECT_ACCESS_METHOD;
+ 					n->objects = list_make1(list_make1(makeString($6)));
+ 					n->arguments = NIL;
+ 					n->missing_ok = true;
+ 					n->behavior = $7;
+ 					n->concurrent = false;
+ 					$$ = (Node *) n;
+ 				}
+ 		;
+ 
+ 
+ /*****************************************************************************
+  *
   *		QUERIES :
   *				CREATE TRIGGER ...
   *				DROP TRIGGER ...
*************** unreserved_keyword:
*** 13778,13783 ****
--- 13829,13835 ----
  			| MATCH
  			| MATERIALIZED
  			| MAXVALUE
+ 			| METHOD
  			| MINUTE_P
  			| MINVALUE
  			| MODE
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
new file mode 100644
index dc431c7..d92864c
*** a/src/backend/parser/parse_utilcmd.c
--- b/src/backend/parser/parse_utilcmd.c
*************** transformIndexConstraint(Constraint *con
*** 1709,1715 ****
  		 * else dump and reload will produce a different index (breaking
  		 * pg_upgrade in particular).
  		 */
! 		if (index_rel->rd_rel->relam != get_am_oid(DEFAULT_INDEX_TYPE, false))
  			ereport(ERROR,
  					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
  					 errmsg("index \"%s\" is not a btree", index_name),
--- 1709,1715 ----
  		 * else dump and reload will produce a different index (breaking
  		 * pg_upgrade in particular).
  		 */
! 		if (index_rel->rd_rel->relam != get_am_oid(DEFAULT_INDEX_TYPE, 'i', false))
  			ereport(ERROR,
  					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
  					 errmsg("index \"%s\" is not a btree", index_name),
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
new file mode 100644
index 045f7f0..4d0aac9
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
*************** ProcessUtilitySlow(Node *parsetree,
*** 1520,1525 ****
--- 1520,1529 ----
  				address = ExecSecLabelStmt((SecLabelStmt *) parsetree);
  				break;
  
+ 			case T_CreateAmStmt:
+ 				address = CreateAccessMethod((CreateAmStmt *) parsetree);
+ 				break;
+ 
  			default:
  				elog(ERROR, "unrecognized node type: %d",
  					 (int) nodeTag(parsetree));
*************** CreateCommandTag(Node *parsetree)
*** 2160,2165 ****
--- 2164,2172 ----
  				case OBJECT_TRANSFORM:
  					tag = "DROP TRANSFORM";
  					break;
+ 				case OBJECT_ACCESS_METHOD:
+ 					tag = "DROP ACCESS METHOD";
+ 					break;
  				default:
  					tag = "???";
  			}
*************** CreateCommandTag(Node *parsetree)
*** 2256,2261 ****
--- 2263,2271 ----
  				case OBJECT_COLLATION:
  					tag = "CREATE COLLATION";
  					break;
+ 				case OBJECT_ACCESS_METHOD:
+ 					tag = "CREATE ACCESS METHOD";
+ 					break;
  				default:
  					tag = "???";
  			}
*************** CreateCommandTag(Node *parsetree)
*** 2519,2524 ****
--- 2529,2538 ----
  			tag = "ALTER POLICY";
  			break;
  
+ 		case T_CreateAmStmt:
+ 			tag = "CREATE ACCESS METHOD";
+ 			break;
+ 
  		case T_PrepareStmt:
  			tag = "PREPARE";
  			break;
*************** GetCommandLogLevel(Node *parsetree)
*** 3076,3081 ****
--- 3090,3099 ----
  			lev = LOGSTMT_DDL;
  			break;
  
+ 		case T_CreateAmStmt:
+ 			lev = LOGSTMT_DDL;
+ 			break;
+ 
  			/* already-planned queries */
  		case T_PlannedStmt:
  			{
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
new file mode 100644
index 46c95b0..516af92
*** a/src/backend/utils/adt/selfuncs.c
--- b/src/backend/utils/adt/selfuncs.c
*************** string_to_bytea_const(const char *str, s
*** 6012,6032 ****
   *-------------------------------------------------------------------------
   */
  
! /*
!  * deconstruct_indexquals is a simple function to examine the indexquals
!  * attached to a proposed IndexPath.  It returns a list of IndexQualInfo
!  * structs, one per qual expression.
!  */
! typedef struct
! {
! 	RestrictInfo *rinfo;		/* the indexqual itself */
! 	int			indexcol;		/* zero-based index column number */
! 	bool		varonleft;		/* true if index column is on left of qual */
! 	Oid			clause_op;		/* qual's operator OID, if relevant */
! 	Node	   *other_operand;	/* non-index operand of qual's operator */
! } IndexQualInfo;
! 
! static List *
  deconstruct_indexquals(IndexPath *path)
  {
  	List	   *result = NIL;
--- 6012,6018 ----
   *-------------------------------------------------------------------------
   */
  
! List *
  deconstruct_indexquals(IndexPath *path)
  {
  	List	   *result = NIL;
*************** orderby_operands_eval_cost(PlannerInfo *
*** 6176,6210 ****
  	return qual_arg_cost;
  }
  
! /*
!  * genericcostestimate is a general-purpose estimator that can be used for
!  * most index types.  In some cases we use genericcostestimate as the base
!  * code and then incorporate additional index-type-specific knowledge in
!  * the type-specific calling function.  To avoid code duplication, we make
!  * genericcostestimate return a number of intermediate values as well as
!  * its preliminary estimates of the output cost values.  The GenericCosts
!  * struct includes all these values.
!  *
!  * Callers should initialize all fields of GenericCosts to zero.  In addition,
!  * they can set numIndexTuples to some positive value if they have a better
!  * than default way of estimating the number of leaf index tuples visited.
!  */
! typedef struct
! {
! 	/* These are the values the cost estimator must return to the planner */
! 	Cost		indexStartupCost;		/* index-related startup cost */
! 	Cost		indexTotalCost; /* total index-related scan cost */
! 	Selectivity indexSelectivity;		/* selectivity of index */
! 	double		indexCorrelation;		/* order correlation of index */
! 
! 	/* Intermediate values we obtain along the way */
! 	double		numIndexPages;	/* number of leaf pages visited */
! 	double		numIndexTuples; /* number of leaf tuples visited */
! 	double		spc_random_page_cost;	/* relevant random_page_cost value */
! 	double		num_sa_scans;	/* # indexscans from ScalarArrayOps */
! } GenericCosts;
! 
! static void
  genericcostestimate(PlannerInfo *root,
  					IndexPath *path,
  					double loop_count,
--- 6162,6168 ----
  	return qual_arg_cost;
  }
  
! void
  genericcostestimate(PlannerInfo *root,
  					IndexPath *path,
  					double loop_count,
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
new file mode 100644
index f798b15..1acd91a
*** a/src/bin/pg_dump/common.c
--- b/src/bin/pg_dump/common.c
*************** getSchemaData(Archive *fout, int *numTab
*** 98,103 ****
--- 98,104 ----
  	int			numProcLangs;
  	int			numCasts;
  	int			numTransforms;
+ 	int			numAccessMethods;
  	int			numOpclasses;
  	int			numOpfamilies;
  	int			numConversions;
*************** getSchemaData(Archive *fout, int *numTab
*** 169,174 ****
--- 170,179 ----
  	oprinfoindex = buildIndexArray(oprinfo, numOperators, sizeof(OprInfo));
  
  	if (g_verbose)
+ 		write_msg(NULL, "reading user-defined access methods\n");
+ 	getAccessMethods(fout, &numAccessMethods);
+ 
+ 	if (g_verbose)
  		write_msg(NULL, "reading user-defined operator classes\n");
  	getOpclasses(fout, &numOpclasses);
  
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
new file mode 100644
index 64c2673..8999bcc
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
*************** static void dumpFunc(Archive *fout, Func
*** 173,178 ****
--- 173,179 ----
  static void dumpCast(Archive *fout, CastInfo *cast);
  static void dumpTransform(Archive *fout, TransformInfo *transform);
  static void dumpOpr(Archive *fout, OprInfo *oprinfo);
+ static void dumpAccessMethod(Archive *fout, AccessMethodInfo *oprinfo);
  static void dumpOpclass(Archive *fout, OpclassInfo *opcinfo);
  static void dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo);
  static void dumpCollation(Archive *fout, CollInfo *convinfo);
*************** getConversions(Archive *fout, int *numCo
*** 4101,4106 ****
--- 4102,4183 ----
  }
  
  /*
+  * getAccessMethods:
+  *	  read all user-defined access methods in the system catalogs and return
+  *    them in the AccessMethodInfo* structure
+  *
+  *	numAccessMethods is set to the number of access methods read in
+  */
+ AccessMethodInfo *
+ getAccessMethods(Archive *fout, int *numAccessMethods)
+ {
+ 	DumpOptions *dopt = fout->dopt;
+ 	PGresult   *res;
+ 	int			ntups;
+ 	int			i;
+ 	PQExpBuffer query;
+ 	AccessMethodInfo *aminfo;
+ 	int			i_tableoid;
+ 	int			i_oid;
+ 	int			i_amname;
+ 	int			i_amhandler;
+ 
+ 	/* Before 9.6, there are no user-defined access methods */
+ 	if (fout->remoteVersion < 90600)
+ 	{
+ 		*numAccessMethods = 0;
+ 		return NULL;
+ 	}
+ 
+ 	query = createPQExpBuffer();
+ 
+ 	/* Make sure we are in proper schema */
+ 	selectSourceSchema(fout, "pg_catalog");
+ 
+ 	/*
+ 	 * Select only user-defined access methods assuming all built-in access
+ 	 * methods have oid < 10000.
+ 	 */
+ 	appendPQExpBuffer(query, "SELECT tableoid, oid, amname, "
+ 					  "amhandler::pg_catalog.regproc AS amhandler "
+ 					  "FROM pg_am "
+ 					  "WHERE oid >= 10000");
+ 
+ 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+ 
+ 	ntups = PQntuples(res);
+ 	*numAccessMethods = ntups;
+ 
+ 	aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
+ 
+ 	i_tableoid = PQfnumber(res, "tableoid");
+ 	i_oid = PQfnumber(res, "oid");
+ 	i_amname = PQfnumber(res, "amname");
+ 	i_amhandler = PQfnumber(res, "amhandler");
+ 
+ 	for (i = 0; i < ntups; i++)
+ 	{
+ 		aminfo[i].dobj.objType = DO_ACCESS_METHOD;
+ 		aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ 		aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ 		AssignDumpId(&aminfo[i].dobj);
+ 		aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
+ 		aminfo[i].dobj.namespace = NULL;
+ 		aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
+ 
+ 		/* Decide whether we want to dump it */
+ 		selectDumpableObject(&(aminfo[i].dobj), dopt);
+ 	}
+ 
+ 	PQclear(res);
+ 
+ 	destroyPQExpBuffer(query);
+ 
+ 	return aminfo;
+ }
+ 
+ 
+ /*
   * getOpclasses:
   *	  read all opclasses in the system catalogs and return them in the
   * OpclassInfo* structure
*************** dumpDumpableObject(Archive *fout, Dumpab
*** 8408,8413 ****
--- 8485,8493 ----
  		case DO_OPERATOR:
  			dumpOpr(fout, (OprInfo *) dobj);
  			break;
+ 		case DO_ACCESS_METHOD:
+ 			dumpAccessMethod(fout, (AccessMethodInfo *) dobj);
+ 			break;
  		case DO_OPCLASS:
  			dumpOpclass(fout, (OpclassInfo *) dobj);
  			break;
*************** convertTSFunction(Archive *fout, Oid fun
*** 11446,11451 ****
--- 11526,11584 ----
  	return result;
  }
  
+ /*
+  * dumpAccessMethod
+  *	  write out a single access method definition
+  */
+ static void
+ dumpAccessMethod(Archive *fout, AccessMethodInfo *aminfo)
+ {
+ 	DumpOptions *dopt = fout->dopt;
+ 	PQExpBuffer q;
+ 	PQExpBuffer delq;
+ 	PQExpBuffer labelq;
+ 	char	   *qamname;
+ 
+ 	/* Skip if not to be dumped */
+ 	if (!aminfo->dobj.dump || dopt->dataOnly)
+ 		return;
+ 
+ 	q = createPQExpBuffer();
+ 	delq = createPQExpBuffer();
+ 	labelq = createPQExpBuffer();
+ 
+ 	qamname = pg_strdup(fmtId(aminfo->dobj.name));
+ 
+ 	appendPQExpBuffer(q, "CREATE ACCESS METHOD %s HANDLER %s;\n",
+ 					  qamname, aminfo->amhandler);
+ 
+ 	appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
+ 					  qamname);
+ 
+ 	appendPQExpBuffer(labelq, "ACCESS METHOD %s",
+ 					  qamname);
+ 
+ 	ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
+ 				 aminfo->dobj.name,
+ 				 NULL,
+ 				 NULL,
+ 				 "",
+ 				 false, "ACCESS METHOD", SECTION_PRE_DATA,
+ 				 q->data, delq->data, NULL,
+ 				 NULL, 0,
+ 				 NULL, NULL);
+ 
+ 	/* Dump Access Method Comments */
+ 	dumpComment(fout, labelq->data,
+ 				NULL, "",
+ 				aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
+ 
+ 	free(qamname);
+ 
+ 	destroyPQExpBuffer(q);
+ 	destroyPQExpBuffer(delq);
+ 	destroyPQExpBuffer(labelq);
+ }
  
  /*
   * dumpOpclass
*************** addBoundaryDependencies(DumpableObject *
*** 16227,16232 ****
--- 16360,16366 ----
  			case DO_FUNC:
  			case DO_AGG:
  			case DO_OPERATOR:
+ 			case DO_ACCESS_METHOD:
  			case DO_OPCLASS:
  			case DO_OPFAMILY:
  			case DO_COLLATION:
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
new file mode 100644
index 9a1d8f8..0fb6087
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
*************** typedef enum
*** 48,53 ****
--- 48,54 ----
  	DO_FUNC,
  	DO_AGG,
  	DO_OPERATOR,
+ 	DO_ACCESS_METHOD,
  	DO_OPCLASS,
  	DO_OPFAMILY,
  	DO_COLLATION,
*************** typedef struct _oprInfo
*** 167,172 ****
--- 168,179 ----
  	Oid			oprcode;
  } OprInfo;
  
+ typedef struct _accessMethodInfo
+ {
+ 	DumpableObject dobj;
+ 	char	   *amhandler;
+ } AccessMethodInfo;
+ 
  typedef struct _opclassInfo
  {
  	DumpableObject dobj;
*************** extern TypeInfo *getTypes(Archive *fout,
*** 548,553 ****
--- 555,561 ----
  extern FuncInfo *getFuncs(Archive *fout, int *numFuncs);
  extern AggInfo *getAggregates(Archive *fout, int *numAggregates);
  extern OprInfo *getOperators(Archive *fout, int *numOperators);
+ extern AccessMethodInfo *getAccessMethods(Archive *fout, int *numAccessMethods);
  extern OpclassInfo *getOpclasses(Archive *fout, int *numOpclasses);
  extern OpfamilyInfo *getOpfamilies(Archive *fout, int *numOpfamilies);
  extern CollInfo *getCollations(Archive *fout, int *numCollations);
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
new file mode 100644
index 78ff59c..3bd5bd8
*** a/src/bin/pg_dump/pg_dump_sort.c
--- b/src/bin/pg_dump/pg_dump_sort.c
*************** static const int oldObjectTypePriority[]
*** 45,50 ****
--- 45,51 ----
  	2,							/* DO_FUNC */
  	3,							/* DO_AGG */
  	3,							/* DO_OPERATOR */
+ 	3,							/* DO_ACCESS_METHOD */
  	4,							/* DO_OPCLASS */
  	4,							/* DO_OPFAMILY */
  	4,							/* DO_COLLATION */
*************** static const int newObjectTypePriority[]
*** 95,100 ****
--- 96,102 ----
  	6,							/* DO_FUNC */
  	7,							/* DO_AGG */
  	8,							/* DO_OPERATOR */
+ 	8,							/* DO_ACCESS_METHOD */
  	9,							/* DO_OPCLASS */
  	9,							/* DO_OPFAMILY */
  	3,							/* DO_COLLATION */
*************** describeDumpableObject(DumpableObject *o
*** 1329,1334 ****
--- 1331,1340 ----
  					 "OPERATOR %s  (ID %d OID %u)",
  					 obj->name, obj->dumpId, obj->catId.oid);
  			return;
+ 		case DO_ACCESS_METHOD:
+ 			snprintf(buf, bufsize,
+ 					 "");
+ 			return;
  		case DO_OPCLASS:
  			snprintf(buf, bufsize,
  					 "OPERATOR CLASS %s  (ID %d OID %u)",
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
new file mode 100644
index 049bf9f..ac16740
*** a/src/include/catalog/dependency.h
--- b/src/include/catalog/dependency.h
*************** typedef enum ObjectClass
*** 153,162 ****
  	OCLASS_EXTENSION,			/* pg_extension */
  	OCLASS_EVENT_TRIGGER,		/* pg_event_trigger */
  	OCLASS_POLICY,				/* pg_policy */
! 	OCLASS_TRANSFORM			/* pg_transform */
  } ObjectClass;
  
! #define LAST_OCLASS		OCLASS_TRANSFORM
  
  
  /* in dependency.c */
--- 153,163 ----
  	OCLASS_EXTENSION,			/* pg_extension */
  	OCLASS_EVENT_TRIGGER,		/* pg_event_trigger */
  	OCLASS_POLICY,				/* pg_policy */
! 	OCLASS_TRANSFORM,			/* pg_transform */
! 	OCLASS_AM,					/* pg_am */
  } ObjectClass;
  
! #define LAST_OCLASS		OCLASS_AM
  
  
  /* in dependency.c */
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
new file mode 100644
index f801c3e..605e154
*** a/src/include/catalog/pg_am.h
--- b/src/include/catalog/pg_am.h
*************** CATALOG(pg_am,2601)
*** 35,40 ****
--- 35,47 ----
  {
  	NameData	amname;			/* access method name */
  	regproc		amhandler;		/* handler function */
+ 
+ 	/*----------
+ 	 * Type of access method. Possible values are
+ 	 *		'i': Index access method
+ 	 *----------
+ 	 */
+ 	char		amtype;
  } FormData_pg_am;
  
  /* ----------------
*************** typedef FormData_pg_am *Form_pg_am;
*** 48,78 ****
   *		compiler constants for pg_am
   * ----------------
   */
! #define Natts_pg_am						2
  #define Anum_pg_am_amname				1
  #define Anum_pg_am_amhandler			2
  
  /* ----------------
   *		initial contents of pg_am
   * ----------------
   */
  
! DATA(insert OID = 403 (  btree		bthandler ));
  DESCR("b-tree index access method");
  #define BTREE_AM_OID 403
! DATA(insert OID = 405 (  hash		hashhandler ));
  DESCR("hash index access method");
  #define HASH_AM_OID 405
! DATA(insert OID = 783 (  gist		gisthandler ));
  DESCR("GiST index access method");
  #define GIST_AM_OID 783
! DATA(insert OID = 2742 (  gin		ginhandler ));
  DESCR("GIN index access method");
  #define GIN_AM_OID 2742
! DATA(insert OID = 4000 (  spgist	spghandler ));
  DESCR("SP-GiST index access method");
  #define SPGIST_AM_OID 4000
! DATA(insert OID = 3580 (  brin		brinhandler ));
  DESCR("block range index (BRIN) access method");
  #define BRIN_AM_OID 3580
  
--- 55,86 ----
   *		compiler constants for pg_am
   * ----------------
   */
! #define Natts_pg_am						3
  #define Anum_pg_am_amname				1
  #define Anum_pg_am_amhandler			2
+ #define Anum_pg_am_amtype				3
  
  /* ----------------
   *		initial contents of pg_am
   * ----------------
   */
  
! DATA(insert OID = 403 (  btree		bthandler	i ));
  DESCR("b-tree index access method");
  #define BTREE_AM_OID 403
! DATA(insert OID = 405 (  hash		hashhandler	i ));
  DESCR("hash index access method");
  #define HASH_AM_OID 405
! DATA(insert OID = 783 (  gist		gisthandler	i ));
  DESCR("GiST index access method");
  #define GIST_AM_OID 783
! DATA(insert OID = 2742 (  gin		ginhandler	i ));
  DESCR("GIN index access method");
  #define GIN_AM_OID 2742
! DATA(insert OID = 4000 (  spgist	spghandler	i ));
  DESCR("SP-GiST index access method");
  #define SPGIST_AM_OID 4000
! DATA(insert OID = 3580 (  brin		brinhandler	i ));
  DESCR("block range index (BRIN) access method");
  #define BRIN_AM_OID 3580
  
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
new file mode 100644
index 54f67e9..1e21f03
*** a/src/include/commands/defrem.h
--- b/src/include/commands/defrem.h
*************** extern void IsThereOpClassInNamespace(co
*** 91,98 ****
  						  Oid opcnamespace);
  extern void IsThereOpFamilyInNamespace(const char *opfname, Oid opfmethod,
  						   Oid opfnamespace);
- extern Oid	get_am_oid(const char *amname, bool missing_ok);
- extern char *get_am_name(Oid amOid);
  extern Oid	get_opclass_oid(Oid amID, List *opclassname, bool missing_ok);
  extern Oid	get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok);
  
--- 91,96 ----
*************** extern Datum transformGenericOptions(Oid
*** 137,142 ****
--- 135,146 ----
  						List *options,
  						Oid fdwvalidator);
  
+ /* commands/amcmds.c */
+ extern ObjectAddress CreateAccessMethod(CreateAmStmt *stmt);
+ extern void RemoveAccessMethodById(Oid amOid);
+ extern Oid	get_am_oid(const char *amname, char amtype, bool missing_ok);
+ extern char *get_am_name(Oid amOid);
+ 
  /* support routines in commands/define.c */
  
  extern char *defGetString(DefElem *def);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
new file mode 100644
index c407fa2..7d46763
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
*************** typedef enum NodeTag
*** 386,391 ****
--- 386,392 ----
  	T_CreatePolicyStmt,
  	T_AlterPolicyStmt,
  	T_CreateTransformStmt,
+ 	T_CreateAmStmt,
  
  	/*
  	 * TAGS FOR PARSE TREE NODES (parsenodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
new file mode 100644
index 2fd0629..8b958b4
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
*************** typedef struct SetOperationStmt
*** 1379,1384 ****
--- 1379,1385 ----
  
  typedef enum ObjectType
  {
+ 	OBJECT_ACCESS_METHOD,
  	OBJECT_AGGREGATE,
  	OBJECT_AMOP,
  	OBJECT_AMPROC,
*************** typedef struct AlterPolicyStmt
*** 2070,2075 ****
--- 2071,2088 ----
  	Node	   *with_check;		/* the policy's WITH CHECK condition. */
  } AlterPolicyStmt;
  
+ /*----------------------
+  *		Create ACCESS METHOD Statement
+  *----------------------
+  */
+ typedef struct CreateAmStmt
+ {
+ 	NodeTag		type;
+ 	char	   *amname;			/* access method name */
+ 	List	   *handler_name;	/* handler function name */
+ 	char		amtype;			/* type of access method */
+ } CreateAmStmt;
+ 
  /* ----------------------
   *		Create TRIGGER Statement
   * ----------------------
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
new file mode 100644
index 6e1e820..7de3404
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
*************** PG_KEYWORD("mapping", MAPPING, UNRESERVE
*** 239,244 ****
--- 239,245 ----
  PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD)
  PG_KEYWORD("materialized", MATERIALIZED, UNRESERVED_KEYWORD)
  PG_KEYWORD("maxvalue", MAXVALUE, UNRESERVED_KEYWORD)
+ PG_KEYWORD("method", METHOD, UNRESERVED_KEYWORD)
  PG_KEYWORD("minute", MINUTE_P, UNRESERVED_KEYWORD)
  PG_KEYWORD("minvalue", MINVALUE, UNRESERVED_KEYWORD)
  PG_KEYWORD("mode", MODE, UNRESERVED_KEYWORD)
diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h
new file mode 100644
index 06fbca7..7fb7466
*** a/src/include/utils/selfuncs.h
--- b/src/include/utils/selfuncs.h
*************** typedef enum
*** 95,100 ****
--- 95,142 ----
  	Pattern_Prefix_None, Pattern_Prefix_Partial, Pattern_Prefix_Exact
  } Pattern_Prefix_Status;
  
+ /*
+  * deconstruct_indexquals is a simple function to examine the indexquals
+  * attached to a proposed IndexPath.  It returns a list of IndexQualInfo
+  * structs, one per qual expression.
+  */
+ typedef struct
+ {
+ 	RestrictInfo *rinfo;		/* the indexqual itself */
+ 	int			indexcol;		/* zero-based index column number */
+ 	bool		varonleft;		/* true if index column is on left of qual */
+ 	Oid			clause_op;		/* qual's operator OID, if relevant */
+ 	Node	   *other_operand;	/* non-index operand of qual's operator */
+ } IndexQualInfo;
+ 
+ /*
+  * genericcostestimate is a general-purpose estimator that can be used for
+  * most index types.  In some cases we use genericcostestimate as the base
+  * code and then incorporate additional index-type-specific knowledge in
+  * the type-specific calling function.  To avoid code duplication, we make
+  * genericcostestimate return a number of intermediate values as well as
+  * its preliminary estimates of the output cost values.  The GenericCosts
+  * struct includes all these values.
+  *
+  * Callers should initialize all fields of GenericCosts to zero.  In addition,
+  * they can set numIndexTuples to some positive value if they have a better
+  * than default way of estimating the number of leaf index tuples visited.
+  */
+ typedef struct
+ {
+ 	/* These are the values the cost estimator must return to the planner */
+ 	Cost		indexStartupCost;		/* index-related startup cost */
+ 	Cost		indexTotalCost; /* total index-related scan cost */
+ 	Selectivity indexSelectivity;		/* selectivity of index */
+ 	double		indexCorrelation;		/* order correlation of index */
+ 
+ 	/* Intermediate values we obtain along the way */
+ 	double		numIndexPages;	/* number of leaf pages visited */
+ 	double		numIndexTuples; /* number of leaf tuples visited */
+ 	double		spc_random_page_cost;	/* relevant random_page_cost value */
+ 	double		num_sa_scans;	/* # indexscans from ScalarArrayOps */
+ } GenericCosts;
+ 
  /* Hooks for plugins to get control when we ask for stats */
  typedef bool (*get_relation_stats_hook_type) (PlannerInfo *root,
  														  RangeTblEntry *rte,
*************** extern double estimate_num_groups(Planne
*** 191,196 ****
--- 233,244 ----
  extern Selectivity estimate_hash_bucketsize(PlannerInfo *root, Node *hashkey,
  						 double nbuckets);
  
+ extern List *deconstruct_indexquals(IndexPath *path);
+ extern void genericcostestimate(PlannerInfo *root, IndexPath *path,
+ 								double loop_count,
+ 								List *qinfos,
+ 								GenericCosts *costs);
+ 
  /* Functions in array_selfuncs.c */
  
  extern Selectivity scalararraysel_containment(PlannerInfo *root,
diff --git a/src/test/regress/expected/create_am.out b/src/test/regress/expected/create_am.out
new file mode 100644
index ...47d6024
*** a/src/test/regress/expected/create_am.out
--- b/src/test/regress/expected/create_am.out
***************
*** 0 ****
--- 1,108 ----
+ --
+ -- Create access method tests
+ --
+ -- Make gist2 over gisthandler. In fact, it would be a synonym to gist.
+ CREATE ACCESS METHOD gist2 TYPE INDEX HANDLER gisthandler;
+ -- Drop old index on fast_emp4000
+ DROP INDEX grect2ind;
+ -- Try to create gist2 index on fast_emp4000: fail because opclass doesn't exist
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ ERROR:  data type box has no default operator class for access method "gist2"
+ HINT:  You must specify an operator class for the index or define a default operator class for the data type.
+ -- Make operator class for boxes using gist2
+ CREATE OPERATOR CLASS box_ops DEFAULT
+ 	FOR TYPE box USING gist2 AS
+ 	OPERATOR 1	<<,
+ 	OPERATOR 2	&<,
+ 	OPERATOR 3	&&,
+ 	OPERATOR 4	&>,
+ 	OPERATOR 5	>>,
+ 	OPERATOR 6	~=,
+ 	OPERATOR 7	@>,
+ 	OPERATOR 8	<@,
+ 	OPERATOR 9	&<|,
+ 	OPERATOR 10	<<|,
+ 	OPERATOR 11	|>>,
+ 	OPERATOR 12	|&>,
+ 	OPERATOR 13	~,
+ 	OPERATOR 14	@,
+ 	FUNCTION 1	gist_box_consistent(internal, box, smallint, oid, internal),
+ 	FUNCTION 2	gist_box_union(internal, internal),
+ 	FUNCTION 3	gist_box_compress(internal),
+ 	FUNCTION 4	gist_box_decompress(internal),
+ 	FUNCTION 5	gist_box_penalty(internal, internal, internal),
+ 	FUNCTION 6	gist_box_picksplit(internal, internal),
+ 	FUNCTION 7	gist_box_same(box, box, internal),
+ 	FUNCTION 9	gist_box_fetch(internal);
+ -- Create gist2 index on fast_emp4000
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ -- Now check the results from plain indexscan
+ SET enable_seqscan = OFF;
+ SET enable_indexscan = ON;
+ SET enable_bitmapscan = OFF;
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+                            QUERY PLAN                           
+ ----------------------------------------------------------------
+  Sort
+    Sort Key: ((home_base[0])[0])
+    ->  Index Only Scan using grect2ind on fast_emp4000
+          Index Cond: (home_base @ '(2000,1000),(200,200)'::box)
+ (4 rows)
+ 
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+        home_base       
+ -----------------------
+  (337,455),(240,359)
+  (1444,403),(1346,344)
+ (2 rows)
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+                          QUERY PLAN                          
+ -------------------------------------------------------------
+  Aggregate
+    ->  Index Only Scan using grect2ind on fast_emp4000
+          Index Cond: (home_base && '(1000,1000),(0,0)'::box)
+ (3 rows)
+ 
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+  count 
+ -------
+      2
+ (1 row)
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+                       QUERY PLAN                       
+ -------------------------------------------------------
+  Aggregate
+    ->  Index Only Scan using grect2ind on fast_emp4000
+          Index Cond: (home_base IS NULL)
+ (3 rows)
+ 
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+  count 
+ -------
+    278
+ (1 row)
+ 
+ -- Try to drop access method: fail because of depending objects
+ DROP ACCESS METHOD gist2;
+ ERROR:  cannot drop access method gist2 because other objects depend on it
+ DETAIL:  operator class box_ops for access method gist2 depends on access method gist2
+ index grect2ind depends on operator class box_ops for access method gist2
+ HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+ -- Drop access method cascade
+ DROP ACCESS METHOD gist2 CASCADE;
+ NOTICE:  drop cascades to 2 other objects
+ DETAIL:  drop cascades to operator class box_ops for access method gist2
+ drop cascades to index grect2ind
+ -- Reset optimizer options
+ RESET enable_seqscan;
+ RESET enable_indexscan;
+ RESET enable_bitmapscan;
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
new file mode 100644
index eb0bc88..2c5be4b
*** a/src/test/regress/expected/sanity_check.out
--- b/src/test/regress/expected/sanity_check.out
*************** e_star|f
*** 44,50 ****
  emp|f
  equipment_r|f
  f_star|f
! fast_emp4000|t
  float4_tbl|f
  float8_tbl|f
  func_index_heap|t
--- 44,50 ----
  emp|f
  equipment_r|f
  f_star|f
! fast_emp4000|f
  float4_tbl|f
  float8_tbl|f
  func_index_heap|t
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
new file mode 100644
index bec0316..8be4b83
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
*************** test: create_index create_view
*** 60,66 ****
  # ----------
  # Another group of parallel tests
  # ----------
! test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes
  
  # ----------
  # sanity_check does a vacuum, affecting the sort order of SELECT *
--- 60,66 ----
  # ----------
  # Another group of parallel tests
  # ----------
! test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes create_am
  
  # ----------
  # sanity_check does a vacuum, affecting the sort order of SELECT *
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
new file mode 100644
index 7e9b319..1de3da8
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
*************** test: drop_if_exists
*** 75,80 ****
--- 75,81 ----
  test: updatable_views
  test: rolenames
  test: roleattributes
+ test: create_am
  test: sanity_check
  test: errors
  test: select
diff --git a/src/test/regress/sql/create_am.sql b/src/test/regress/sql/create_am.sql
new file mode 100644
index ...e2051c5
*** a/src/test/regress/sql/create_am.sql
--- b/src/test/regress/sql/create_am.sql
***************
*** 0 ****
--- 1,73 ----
+ --
+ -- Create access method tests
+ --
+ 
+ -- Make gist2 over gisthandler. In fact, it would be a synonym to gist.
+ CREATE ACCESS METHOD gist2 TYPE INDEX HANDLER gisthandler;
+ 
+ -- Drop old index on fast_emp4000
+ DROP INDEX grect2ind;
+ 
+ -- Try to create gist2 index on fast_emp4000: fail because opclass doesn't exist
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ 
+ -- Make operator class for boxes using gist2
+ CREATE OPERATOR CLASS box_ops DEFAULT
+ 	FOR TYPE box USING gist2 AS
+ 	OPERATOR 1	<<,
+ 	OPERATOR 2	&<,
+ 	OPERATOR 3	&&,
+ 	OPERATOR 4	&>,
+ 	OPERATOR 5	>>,
+ 	OPERATOR 6	~=,
+ 	OPERATOR 7	@>,
+ 	OPERATOR 8	<@,
+ 	OPERATOR 9	&<|,
+ 	OPERATOR 10	<<|,
+ 	OPERATOR 11	|>>,
+ 	OPERATOR 12	|&>,
+ 	OPERATOR 13	~,
+ 	OPERATOR 14	@,
+ 	FUNCTION 1	gist_box_consistent(internal, box, smallint, oid, internal),
+ 	FUNCTION 2	gist_box_union(internal, internal),
+ 	FUNCTION 3	gist_box_compress(internal),
+ 	FUNCTION 4	gist_box_decompress(internal),
+ 	FUNCTION 5	gist_box_penalty(internal, internal, internal),
+ 	FUNCTION 6	gist_box_picksplit(internal, internal),
+ 	FUNCTION 7	gist_box_same(box, box, internal),
+ 	FUNCTION 9	gist_box_fetch(internal);
+ 
+ -- Create gist2 index on fast_emp4000
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ 
+ -- Now check the results from plain indexscan
+ SET enable_seqscan = OFF;
+ SET enable_indexscan = ON;
+ SET enable_bitmapscan = OFF;
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+ 
+ -- Try to drop access method: fail because of depending objects
+ DROP ACCESS METHOD gist2;
+ 
+ -- Drop access method cascade
+ DROP ACCESS METHOD gist2 CASCADE;
+ 
+ -- Reset optimizer options
+ RESET enable_seqscan;
+ RESET enable_indexscan;
+ RESET enable_bitmapscan;
generic-xlog.8.patchapplication/octet-stream; name=generic-xlog.8.patchDownload
diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
new file mode 100644
index c72a1f2..c0e38fd
*** a/src/backend/access/rmgrdesc/Makefile
--- b/src/backend/access/rmgrdesc/Makefile
*************** subdir = src/backend/access/rmgrdesc
*** 8,16 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o gindesc.o gistdesc.o \
! 	   hashdesc.o heapdesc.o mxactdesc.o nbtdesc.o relmapdesc.o \
! 	   replorigindesc.o seqdesc.o smgrdesc.o spgdesc.o \
  	   standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
  
  include $(top_srcdir)/src/backend/common.mk
--- 8,16 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o genericdesc.o \
! 	   gindesc.o gistdesc.o hashdesc.o heapdesc.o mxactdesc.o nbtdesc.o \
! 	   relmapdesc.o replorigindesc.o seqdesc.o smgrdesc.o spgdesc.o \
  	   standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
  
  include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/genericdesc.c b/src/backend/access/rmgrdesc/genericdesc.c
new file mode 100644
index ...3d035c2
*** a/src/backend/access/rmgrdesc/genericdesc.c
--- b/src/backend/access/rmgrdesc/genericdesc.c
***************
*** 0 ****
--- 1,58 ----
+ /*-------------------------------------------------------------------------
+  *
+  * genericdesc.c
+  *	  rmgr descriptor routines for access/transam/generic_xlog.c
+  *
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/backend/access/rmgrdesc/genericdesc.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/generic_xlog.h"
+ #include "lib/stringinfo.h"
+ #include "storage/relfilenode.h"
+ 
+ /*
+  * Description of generic xlog record: write page regions which this record
+  * overrides.
+  */
+ void
+ generic_desc(StringInfo buf, XLogReaderState *record)
+ {
+ 	Pointer		ptr = XLogRecGetData(record),
+ 				end = ptr + XLogRecGetDataLen(record);
+ 
+ 	while (ptr < end)
+ 	{
+ 		OffsetNumber	offset,
+ 						length;
+ 
+ 		memcpy(&offset, ptr, sizeof(offset));
+ 		ptr += sizeof(offset);
+ 		memcpy(&length, ptr, sizeof(length));
+ 		ptr += sizeof(length);
+ 		ptr += length;
+ 
+ 		if (ptr < end)
+ 			appendStringInfo(buf, "offset %u, length %u; ", offset, length);
+ 		else
+ 			appendStringInfo(buf, "offset %u, length %u", offset, length);
+ 	}
+ 
+ 	return;
+ }
+ 
+ /*
+  * Identification of generic xlog record: we don't distinguish any subtypes
+  * inside generic xlog records.
+  */
+ const char *
+ generic_identify(uint8 info)
+ {
+ 	return "Generic";
+ }
diff --git a/src/backend/access/transam/Makefile b/src/backend/access/transam/Makefile
new file mode 100644
index 94455b2..16fbe47
*** a/src/backend/access/transam/Makefile
--- b/src/backend/access/transam/Makefile
*************** subdir = src/backend/access/transam
*** 12,19 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = clog.o commit_ts.o multixact.o parallel.o rmgr.o slru.o subtrans.o \
! 	timeline.o transam.o twophase.o twophase_rmgr.o varsup.o \
  	xact.o xlog.o xlogarchive.o xlogfuncs.o \
  	xloginsert.o xlogreader.o xlogutils.o
  
--- 12,19 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = clog.o commit_ts.o generic_xlog.o multixact.o parallel.o rmgr.o slru.o \
! 	subtrans.o timeline.o transam.o twophase.o twophase_rmgr.o varsup.o \
  	xact.o xlog.o xlogarchive.o xlogfuncs.o \
  	xloginsert.o xlogreader.o xlogutils.o
  
diff --git a/src/backend/access/transam/generic_xlog.c b/src/backend/access/transam/generic_xlog.c
new file mode 100644
index ...ef4836b
*** a/src/backend/access/transam/generic_xlog.c
--- b/src/backend/access/transam/generic_xlog.c
***************
*** 0 ****
--- 1,452 ----
+ /*-------------------------------------------------------------------------
+  *
+  * generic_xlog.c
+  *	 Implementation of generic xlog records.
+  *
+  *
+  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/backend/access/transam/generic_xlog.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/generic_xlog.h"
+ #include "access/xlogutils.h"
+ #include "miscadmin.h"
+ #include "utils/memutils.h"
+ 
+ /*---
+  * Delta between pages consists of set of fragments.  Each fragment represents
+  * changes made in given region of page.  Fragment is described as following.
+  *
+  * - offset of page region (OffsetNumber)
+  * - length of page region (OffsetNumber)
+  * - data - the data to place into described region ('length' number of bytes)
+  *
+  * Unchanged regions of page are uncovered by these fragments.  This is why
+  * delta could be more compact than full page image.  But if unchanged region
+  * of page is less than fragment header (offset and length) then it would
+  * increase size of delta instead of decreasing.  Thus, we break fragment only
+  * for unchanged regions greater than MATCH_THRESHOLD.
+  *
+  * The worst case for delta size is when we didn't find any unchanged region
+  * in the page. Then size of delta would be size of page plus size of fragment
+  * header.
+  */
+ #define FRAGMENT_HEADER_SIZE	(2 * sizeof(OffsetNumber))
+ #define MATCH_THRESHOLD			FRAGMENT_HEADER_SIZE
+ #define MAX_DELTA_SIZE			BLCKSZ + FRAGMENT_HEADER_SIZE
+ 
+ /* Struct of generic xlog data for single page */
+ typedef struct
+ {
+ 	Buffer	buffer;			/* registered buffer */
+ 	char	image[BLCKSZ];	/* copy of page image for modification */
+ 	char	data[MAX_DELTA_SIZE]; /* delta between page images */
+ 	int		dataLen;		/* space consumed in data field */
+ 	bool	fullImage;		/* are we taking full image of this page? */
+ } PageData;
+ 
+ /* Enum of generic xlog (gxlog) status */
+ enum GenericXlogStatus
+ {
+ 	GXLOG_NOT_STARTED,	/* gxlog is not started */
+ 	GXLOG_LOGGED,		/* gxlog is started for logged relation */
+ 	GXLOG_UNLOGGED		/* gxlog is started for unlogged relation */
+ };
+ 
+ static enum GenericXlogStatus	genericXlogStatus = GXLOG_NOT_STARTED;
+ static PageData					pages[MAX_GENERIC_XLOG_PAGES];
+ 
+ 
+ static void writeFragment(PageData *pageData, OffsetNumber offset,
+ 						  OffsetNumber len, Pointer data);
+ static void writeDelta(PageData *pageData);
+ static void applyPageRedo(Page page, Pointer data, Size dataSize);
+ 
+ /*
+  * Write next fragment into delta.
+  */
+ static void
+ writeFragment(PageData *pageData, OffsetNumber offset, OffsetNumber length,
+ 			  Pointer data)
+ {
+ 	Pointer			ptr = pageData->data + pageData->dataLen;
+ 
+ 	/* Check we have enough of space */
+ 	Assert(pageData->dataLen + sizeof(offset) +
+ 		   sizeof(length) + length <= sizeof(pageData->data));
+ 
+ 	/* Write fragment data */
+ 	memcpy(ptr, &offset, sizeof(offset));
+ 	ptr += sizeof(offset);
+ 	memcpy(ptr, &length, sizeof(length));
+ 	ptr += sizeof(length);
+ 	memcpy(ptr, data, length);
+ 	ptr += length;
+ 
+ 	pageData->dataLen = ptr - pageData->data;
+ }
+ 
+ /*
+  * Make delta for given page.
+  */
+ static void
+ writeDelta(PageData *pageData)
+ {
+ 	Page			page = BufferGetPage(pageData->buffer),
+ 					image = (Page) pageData->image;
+ 	int				i,
+ 					fragmentBegin = -1,
+ 					fragmentEnd = -1;
+ 	uint16			pageLower = ((PageHeader) page)->pd_lower,
+ 					pageUpper = ((PageHeader) page)->pd_upper,
+ 					imageLower = ((PageHeader) image)->pd_lower,
+ 					imageUpper = ((PageHeader) image)->pd_upper;
+ 
+ 	for (i = 0; i < BLCKSZ; i++)
+ 	{
+ 		bool	match;
+ 
+ 		/*
+ 		 * Check if bytes in old and new page images matches.  We don't rely
+ 		 * data in unallocated area between pd_lower and pd_upper.  Thus we
+ 		 * assume unallocated area to expand with unmatched bytes.  Bytes
+ 		 * inside unallocated area are assumed to always match.
+ 		 */
+ 		if (i < pageLower)
+ 		{
+ 			if (i < imageLower)
+ 				match = (page[i] == image[i]);
+ 			else
+ 				match = false;
+ 		}
+ 		else if (i >= pageUpper)
+ 		{
+ 			if (i >= imageUpper)
+ 				match = (page[i] == image[i]);
+ 			else
+ 				match = false;
+ 		}
+ 		else
+ 		{
+ 			match = true;
+ 		}
+ 
+ 		if (match)
+ 		{
+ 			if (fragmentBegin >= 0)
+ 			{
+ 				/* Matched byte is potential of fragment. */
+ 				if (fragmentEnd < 0)
+ 					fragmentEnd = i;
+ 
+ 				/*
+ 				 * Write next fragment if sequence of matched bytes is longer
+ 				 * than MATCH_THRESHOLD.
+ 				 */
+ 				if (i - fragmentEnd >= MATCH_THRESHOLD)
+ 				{
+ 					writeFragment(pageData, fragmentBegin,
+ 								  fragmentEnd - fragmentBegin,
+ 								  page + fragmentBegin);
+ 					fragmentBegin = -1;
+ 					fragmentEnd = -1;
+ 				}
+ 			}
+ 		}
+ 		else
+ 		{
+ 			/* On unmatched byte, start new fragment if it's not done yet */
+ 			if (fragmentBegin < 0)
+ 				fragmentBegin = i;
+ 			fragmentEnd = -1;
+ 		}
+ 	}
+ 
+ 	if (fragmentBegin >= 0)
+ 		writeFragment(pageData, fragmentBegin,
+ 					  BLCKSZ - fragmentBegin,
+ 					  page + fragmentBegin);
+ 
+ #ifdef WAL_DEBUG
+ 	/*
+ 	 * If xlog debug is enabled then check produced delta.  Result of delta
+ 	 * application to saved image should be the same as current page state.
+ 	 */
+ 	if (XLOG_DEBUG)
+ 	{
+ 		char	tmp[BLCKSZ];
+ 		memcpy(tmp, image, BLCKSZ);
+ 		applyPageRedo(tmp, pageData->data, pageData->dataLen);
+ 		elog(ERROR, "result of generic xlog apply doesn't match");
+ 	}
+ #endif
+ }
+ 
+ /*
+  * Start new generic xlog record.
+  */
+ void
+ GenericXLogStart(Relation relation)
+ {
+ 	int i;
+ 
+ 	if (genericXlogStatus != GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogStart: generic xlog is already started")));
+ 
+ 	genericXlogStatus = RelationNeedsWAL(relation) ? GXLOG_LOGGED : GXLOG_UNLOGGED;
+ 
+ 	for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 	{
+ 		pages[i].buffer = InvalidBuffer;
+ 	}
+ }
+ 
+ /*
+  * Register new buffer for generic xlog record.
+  */
+ Page
+ GenericXLogRegister(Buffer buffer, bool isNew)
+ {
+ 	int block_id;
+ 
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogRegister: generic xlog isn't started")));
+ 
+ 	/* Place new buffer to unused slot in array */
+ 	for (block_id = 0; block_id < MAX_GENERIC_XLOG_PAGES; block_id++)
+ 	{
+ 		if (BufferIsInvalid(pages[block_id].buffer))
+ 		{
+ 			pages[block_id].buffer = buffer;
+ 			memcpy(pages[block_id].image, BufferGetPage(buffer), BLCKSZ);
+ 			pages[block_id].dataLen = 0;
+ 			pages[block_id].fullImage = isNew;
+ 			return (Page)pages[block_id].image;
+ 		}
+ 		else if (pages[block_id].buffer == buffer)
+ 		{
+ 			/* 
+ 			 * Buffer already registered.  Just return image which is already
+ 			 * prepared.
+ 			 */
+ 			return (Page)pages[block_id].image;
+ 		}
+ 	}
+ 
+ 	ereport(ERROR,
+ 			(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ 			 errmsg("GenericXLogRegister: maximum number of %d buffers is exceeded",
+ 					MAX_GENERIC_XLOG_PAGES)));
+ 
+ 	/* keep compiler quiet */
+ 	return NULL;
+ }
+ 
+ /*
+  * Unregister particular buffer for generic xlog record.
+  */
+ void
+ GenericXLogUnregister(Buffer buffer)
+ {
+ 	int block_id;
+ 
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogUnregister: generic xlog isn't started")));
+ 
+ 	/* Find block in array to unregister */
+ 	for (block_id = 0; block_id < MAX_GENERIC_XLOG_PAGES; block_id++)
+ 	{
+ 		if (pages[block_id].buffer == buffer)
+ 		{
+ 			/*
+ 			 * Preserve order of pages in array because it could matter for
+ 			 * concurrency.
+ 			 */
+ 			memmove(&pages[block_id], &pages[block_id + 1],
+ 					(MAX_GENERIC_XLOG_PAGES - block_id - 1) * sizeof(PageData));
+ 			pages[MAX_GENERIC_XLOG_PAGES - 1].buffer = InvalidBuffer;
+ 			return;
+ 		}
+ 	}
+ 
+ 	ereport(ERROR,
+ 			(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 			 errmsg("GenericXLogUnregister: registered buffer not found")));
+ }
+ 
+ /*
+  * Put all changes in registered buffers to generic xlog record.
+  */
+ XLogRecPtr
+ GenericXLogFinish(void)
+ {
+ 	XLogRecPtr lsn = InvalidXLogRecPtr;
+ 	int i;
+ 
+ 	if (genericXlogStatus == GXLOG_LOGGED)
+ 	{
+ 		/* Logged relation: make xlog record in critical section. */
+ 		START_CRIT_SECTION();
+ 		XLogBeginInsert();
+ 
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			char	tmp[BLCKSZ];
+ 
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 
+ 			/* Swap current and saved page image. */
+ 			memcpy(tmp, pages[i].image, BLCKSZ);
+ 			memcpy(pages[i].image, BufferGetPage(pages[i].buffer), BLCKSZ);
+ 			memcpy(BufferGetPage(pages[i].buffer), tmp, BLCKSZ);
+ 
+ 			if (pages[i].fullImage)
+ 			{
+ 				/* Full page image doesn't require anything special from us */
+ 				XLogRegisterBuffer(i, pages[i].buffer, REGBUF_FORCE_IMAGE);
+ 			}
+ 			else
+ 			{
+ 				/*
+ 				 * In normal node calculate delta and write use it as data
+ 				 * associated with this page.
+ 				 */
+ 				XLogRegisterBuffer(i, pages[i].buffer, REGBUF_STANDARD);
+ 				writeDelta(&pages[i]);
+ 				XLogRegisterBufData(i, pages[i].data, pages[i].dataLen);
+ 			}
+ 		}
+ 
+ 		/* Insert xlog record */
+ 		lsn = XLogInsert(RM_GENERIC_ID, 0);
+ 
+ 		/* Set LSN and make buffers dirty */
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 			PageSetLSN(BufferGetPage(pages[i].buffer), lsn);
+ 			MarkBufferDirty(pages[i].buffer);
+ 		}
+ 		END_CRIT_SECTION();
+ 	}
+ 	else if (genericXlogStatus == GXLOG_UNLOGGED)
+ 	{
+ 		/* Unlogged relation: skip xlog-related stuff */
+ 		START_CRIT_SECTION();
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 			memcpy(BufferGetPage(pages[i].buffer), pages[i].image, BLCKSZ);
+ 			MarkBufferDirty(pages[i].buffer);
+ 		}
+ 		END_CRIT_SECTION();
+ 	}
+ 	else
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogFinish: generic xlog isn't started")));
+ 	}
+ 
+ 	genericXlogStatus = GXLOG_NOT_STARTED;
+ 
+ 	return lsn;
+ }
+ 
+ /*
+  * Abort generic xlog record.
+  */
+ void
+ GenericXLogAbort(void)
+ {
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogAbort: generic xlog isn't started")));
+ 
+ 	genericXlogStatus = GXLOG_NOT_STARTED;
+ }
+ 
+ /*
+  * Apply delta to given page image.
+  */
+ static void
+ applyPageRedo(Page page, Pointer data, Size dataSize)
+ {
+ 	Pointer ptr = data, end = data + dataSize;
+ 
+ 	while (ptr < end)
+ 	{
+ 		OffsetNumber	offset,
+ 						length;
+ 
+ 		memcpy(&offset, ptr, sizeof(offset));
+ 		ptr += sizeof(offset);
+ 		memcpy(&length, ptr, sizeof(length));
+ 		ptr += sizeof(length);
+ 
+ 		memcpy(page + offset, ptr, length);
+ 
+ 		ptr += length;
+ 	}
+ }
+ 
+ /*
+  * Redo function for generic xlog record.
+  */
+ void
+ generic_redo(XLogReaderState *record)
+ {
+ 	uint8		block_id;
+ 	Buffer		buffers[MAX_GENERIC_XLOG_PAGES] = {InvalidBuffer};
+ 	XLogRecPtr	lsn = record->EndRecPtr;
+ 
+ 	Assert(record->max_block_id < MAX_GENERIC_XLOG_PAGES);
+ 
+ 	/* Interate over blocks */
+ 	for (block_id = 0; block_id <= record->max_block_id; block_id++)
+ 	{
+ 		XLogRedoAction action;
+ 
+ 		if (!XLogRecHasBlockRef(record, block_id))
+ 			continue;
+ 
+ 		action = XLogReadBufferForRedo(record, block_id, &buffers[block_id]);
+ 
+ 		/* Apply redo to given block if needed */
+ 		if (action == BLK_NEEDS_REDO)
+ 		{
+ 			Pointer	blockData;
+ 			Size	blockDataSize;
+ 			Page	page;
+ 
+ 			page = BufferGetPage(buffers[block_id]);
+ 			blockData = XLogRecGetBlockData(record, block_id, &blockDataSize);
+ 			applyPageRedo(page, blockData, blockDataSize);
+ 
+ 			PageSetLSN(page, lsn);
+ 			MarkBufferDirty(buffers[block_id]);
+ 		}
+ 	}
+ 
+ 	/* Changes are done: unlock and release all buffers */
+ 	for (block_id = 0; block_id <= record->max_block_id; block_id++)
+ 	{
+ 		if (BufferIsValid(buffers[block_id]))
+ 			UnlockReleaseBuffer(buffers[block_id]);
+ 	}
+ }
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
new file mode 100644
index 7c4d773..7b38c16
*** a/src/backend/access/transam/rmgr.c
--- b/src/backend/access/transam/rmgr.c
***************
*** 11,16 ****
--- 11,17 ----
  #include "access/commit_ts.h"
  #include "access/gin.h"
  #include "access/gist_private.h"
+ #include "access/generic_xlog.h"
  #include "access/hash.h"
  #include "access/heapam_xlog.h"
  #include "access/brin_xlog.h"
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
new file mode 100644
index 88c3a49..2d69dc2
*** a/src/backend/replication/logical/decode.c
--- b/src/backend/replication/logical/decode.c
*************** LogicalDecodingProcessRecord(LogicalDeco
*** 135,140 ****
--- 135,141 ----
  		case RM_BRIN_ID:
  		case RM_COMMIT_TS_ID:
  		case RM_REPLORIGIN_ID:
+ 		case RM_GENERIC_ID:
  			break;
  		case RM_NEXT_ID:
  			elog(ERROR, "unexpected RM_NEXT_ID rmgr_id: %u", (RmgrIds) XLogRecGetRmid(buf.record));
diff --git a/src/bin/pg_xlogdump/.gitignore b/src/bin/pg_xlogdump/.gitignore
new file mode 100644
index eebaf30..33a1acf
*** a/src/bin/pg_xlogdump/.gitignore
--- b/src/bin/pg_xlogdump/.gitignore
***************
*** 4,9 ****
--- 4,10 ----
  /clogdesc.c
  /committsdesc.c
  /dbasedesc.c
+ /genericdesc.c
  /gindesc.c
  /gistdesc.c
  /hashdesc.c
diff --git a/src/bin/pg_xlogdump/rmgrdesc.c b/src/bin/pg_xlogdump/rmgrdesc.c
new file mode 100644
index f9cd395..cff7e59
*** a/src/bin/pg_xlogdump/rmgrdesc.c
--- b/src/bin/pg_xlogdump/rmgrdesc.c
***************
*** 11,16 ****
--- 11,17 ----
  #include "access/brin_xlog.h"
  #include "access/clog.h"
  #include "access/commit_ts.h"
+ #include "access/generic_xlog.h"
  #include "access/gin.h"
  #include "access/gist_private.h"
  #include "access/hash.h"
diff --git a/src/include/access/generic_xlog.h b/src/include/access/generic_xlog.h
new file mode 100644
index ...9a10c2c
*** a/src/include/access/generic_xlog.h
--- b/src/include/access/generic_xlog.h
***************
*** 0 ****
--- 1,92 ----
+ /*-------------------------------------------------------------------------
+  *
+  * generic_xlog.h
+  *	  Generic xlog API definition.
+  *
+  *
+  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/access/generic_xlog.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef GENERIC_XLOG_H
+ #define GENERIC_XLOG_H
+ 
+ #include "access/xlog.h"
+ #include "access/xlog_internal.h"
+ #include "storage/bufpage.h"
+ #include "utils/rel.h"
+ 
+ #define MAX_GENERIC_XLOG_PAGES	  3
+ 
+ /*-------------------------------------------------------------------------
+  * API for construction of generic xlog records
+  *
+  * This API allows user to construct generic xlog records which are
+  * describing difference between pages in general way.  Thus it's useful
+  * for extension which provides custom access methods because they couldn't
+  * register their own WAL redo routines.
+  *
+  * Generic xlog record should be constructed in following steps.
+  * 1) GenericXLogStart(relation) - start construction of generic xlog
+  *	  record for given relation.
+  * 2) GenericXLogRegister(buffer, isNew) - register one or more buffers
+  *	  for generic xlog record.  This function return a copy of page image
+  *	  where modifications should be performed.  The second argument
+  *	  indicates that block is new and full image should be taken.
+  * 3) Do modification of page images obtained in previous step.
+  * 4) GenericXLogFinish() - finish construction of generic xlog record.
+  *
+  * Please, note following points while constructing generic xlog records.
+  * - No direct modifications of page images are allowed! All modifications
+  *	 should be done in copies returned by GenericXLogRegister().  Literally
+  *	 code which makes generic xlog records should never call
+  *	 BufferGetPage() function.
+  * - On any step generic xlog record construction could be canceled by
+  *	 calling GenericXLogAbort().  All changes made in page images copies
+  *	 would be discarded.
+  * - Registrations of buffers (step 2) and modifications of page images
+  *	 (step 3) could be mixed in any sequence.  The only restriction is that
+  *	 you can modify page image only after registration of corresponding
+  *	 buffer.
+  * - After registration buffer also can be unregistered by calling
+  *	 GenericXLogUnregister(buffer).  In this case changes made in particular
+  *	 page image copy will be discarded.
+  * - Generic xlog assumes that pages are using standard layout.  I.e. all
+  *	 information between pd_lower and pd_upper will be discarded.
+  * - Maximum number of buffers simultaneously registered for generic xlog
+  *	 is MAX_GENERIC_XLOG_PAGES.  Error would be thrown if this limit
+  *	 exceeded.
+  * - Since you modify copies of page images, GenericXLogStart() doesn't
+  *	 start a critical section.  Thus, you can do memory allocation, error
+  *	 throwing etc between GenericXLogStart() and GenericXLogFinish().
+  *	 Actual critical section present inside GenericXLogFinish().
+  * - GenericXLogFinish() takes care about marking buffers dirty and setting
+  *	 their LSNs.  You don't need to do this explicitly.
+  * - For unlogged relations, everything work the same expect there is no
+  *	 WAL record produced.  Thus, you typically don't need to do any explicit
+  *	 checks for unlogged relations.
+  * - If registered buffer isn't new, generic xlog record contains delta
+  *	 between old and new page images.  This delta is produced by per byte
+  *	 comparison.  Current delta mechanist is not effective for data shift
+  *	 inside the page.  However, it could be improved in further versions.
+  * - Generic xlog redo function will acquire exclusive locks to buffers
+  *	 in the same order they were registered.  After redo of all changes
+  *	 locks would be released in the same order.  That could makes sense for
+  *	 concurrency.
+  *-------------------------------------------------------------------------
+  */
+ extern void GenericXLogStart(Relation relation);
+ extern Page GenericXLogRegister(Buffer buffer, bool isNew);
+ extern void GenericXLogUnregister(Buffer buffer);
+ extern XLogRecPtr GenericXLogFinish(void);
+ extern void GenericXLogAbort(void);
+ 
+ /* functions defined for rmgr */
+ extern void generic_redo(XLogReaderState *record);
+ extern const char *generic_identify(uint8 info);
+ extern void generic_desc(StringInfo buf, XLogReaderState *record);
+ 
+ #endif   /* GENERIC_XLOG_H */
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
new file mode 100644
index fab912d..3cfe6f7
*** a/src/include/access/rmgrlist.h
--- b/src/include/access/rmgrlist.h
*************** PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo
*** 45,47 ****
--- 45,48 ----
  PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL)
  PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL)
  PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL)
+ PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL)
bloom-contrib.8.patchapplication/octet-stream; name=bloom-contrib.8.patchDownload
diff --git a/contrib/Makefile b/contrib/Makefile
new file mode 100644
index bd251f6..3ac3818
*** a/contrib/Makefile
--- b/contrib/Makefile
*************** SUBDIRS = \
*** 8,13 ****
--- 8,14 ----
  		adminpack	\
  		auth_delay	\
  		auto_explain	\
+ 		bloom		\
  		btree_gin	\
  		btree_gist	\
  		chkpass		\
diff --git a/contrib/bloom/.gitignore b/contrib/bloom/.gitignore
new file mode 100644
index ...5dcb3ff
*** a/contrib/bloom/.gitignore
--- b/contrib/bloom/.gitignore
***************
*** 0 ****
--- 1,4 ----
+ # Generated subdirectories
+ /log/
+ /results/
+ /tmp_check/
diff --git a/contrib/bloom/Makefile b/contrib/bloom/Makefile
new file mode 100644
index ...0dbbb6c
*** a/contrib/bloom/Makefile
--- b/contrib/bloom/Makefile
***************
*** 0 ****
--- 1,24 ----
+ # contrib/bloom/Makefile
+ 
+ MODULE_big = bloom
+ OBJS = blcost.o blinsert.o blscan.o blutils.o blvacuum.o blvalidate.o $(WIN32RES)
+ 
+ EXTENSION = bloom
+ DATA = bloom--1.0.sql
+ PGFILEDESC = "bloom access method - signature file based index"
+ 
+ REGRESS = bloom
+ 
+ ifdef USE_PGXS
+ PG_CONFIG = pg_config
+ PGXS := $(shell $(PG_CONFIG) --pgxs)
+ include $(PGXS)
+ else
+ subdir = contrib/bloom
+ top_builddir = ../..
+ include $(top_builddir)/src/Makefile.global
+ include $(top_srcdir)/contrib/contrib-global.mk
+ endif
+ 
+ wal-check:
+ 	$(prove_check)
diff --git a/contrib/bloom/WALTest.pm b/contrib/bloom/WALTest.pm
new file mode 100644
index ...b2daf8b
*** a/contrib/bloom/WALTest.pm
--- b/contrib/bloom/WALTest.pm
***************
*** 0 ****
--- 1,181 ----
+ package WALTest;
+ 
+ # Test driver for generic WAL records.  Test script should consists of following
+ # steps:
+ #
+ # 1. setup_cluster - initialization of master-standby setup
+ #
+ # 2. Initialize some test dataset on master.
+ #
+ # 3. Check that queries gives same result on master and standby.
+ #
+ # 4. Make modification of test dataset on master.
+ #
+ # 5. Check that queries gives same result on master and standby.
+ #
+ # 6. clean_setup - stops both servers used in the test, if they're still
+ # running.
+ #
+ # Steps 4 and 5 could be repeated arbitrary number of times.
+ #
+ # The test script can use the helper functions master_psql and standby_psql
+ # to run psql against the master and standby servers, respectively. Helper
+ # function wait_sync ensures that all changes made in master are applied to
+ # standby.  Helper function check_query runs query on both master and
+ # standby, and check that query results are the same.
+ #
+ # The test script can also use the $connstr_master and $connstr_standby global
+ # variables, which contain libpq connection strings for connecting to the
+ # master and standby servers. The data directories are also available
+ # in paths $test_master_datadir and $test_standby_datadir.
+ 
+ use strict;
+ use warnings;
+ 
+ use Config;
+ use Exporter 'import';
+ use File::Copy;
+ use File::Path qw(rmtree);
+ use IPC::Run qw(run);
+ use PostgresNode;
+ use TestLib;
+ use Test::More;
+ 
+ our @EXPORT = qw(
+   $node_master
+   $node_standby
+ 
+   master_psql
+   standby_psql
+   run_query
+   wait_sync
+   check_query
+ 
+   setup_cluster
+   clean_cluster
+ );
+ 
+ # Our nodes.
+ our $node_master;
+ our $node_standby;
+ 
+ sub master_psql
+ {
+ 	my $cmd = shift;
+ 
+ 	system_or_bail 'psql', '-q', '--no-psqlrc', '-d',
+ 	  $node_master->connstr('postgres'), '-c', "$cmd";
+ }
+ 
+ sub standby_psql
+ {
+ 	my $cmd = shift;
+ 
+ 	system_or_bail 'psql', '-q', '--no-psqlrc', '-d',
+ 	  $node_standby->connstr('postgres'), '-c', "$cmd";
+ }
+ 
+ # Run query on given node and return it result.  Die on any error.
+ sub run_query
+ {
+ 	my ($node, $query) = @_;
+ 	my ($stdout, $stderr, $name);
+ 
+ 	# we want just the output, no formatting
+ 	my $result = run [
+ 		'psql', '-q', '-A', '-t', '--no-psqlrc', '-d',
+ 		$node->connstr('postgres'),
+ 		'-c', $query ],
+ 	  '>', \$stdout, '2>', \$stderr;
+ 
+ 	$name = $node->name;
+ 
+ 	# We don't use ok() for the exit code and stderr, because we want this
+ 	# check to be just a single test.
+ 	if (!$result)
+ 	{
+ 		die "psql exit code on node \"$name\"";
+ 	}
+ 	elsif ($stderr ne '')
+ 	{
+ 		diag $stderr;
+ 		die "psql no stderr on node \"$name\"";
+ 	}
+ 	else
+ 	{
+ 		$stdout =~ s/\r//g if $Config{osname} eq 'msys';
+ 		return $stdout;
+ 	}
+ }
+ 
+ # Wait until standby applies all WAL records from master.
+ sub wait_sync
+ {
+ 	# Get xlog location from master
+ 	my $xlog_location =
+ 		run_query($node_master, "SELECT pg_current_xlog_location();");
+ 	chomp $xlog_location;
+ 
+ 	# Wait until standby apply xlog to at least this location.
+ 	$node_standby->poll_query_until('postgres',
+ 		"SELECT pg_last_xlog_replay_location() >= '$xlog_location';")
+ 	  or die "Timed out while waiting for standby sync";
+ }
+ 
+ # Run a query against both master and standby, and check that the output
+ # is the same.
+ sub check_query
+ {
+ 	my ($query, $test_name) = @_;
+ 	my ($master_result, $standby_result);
+ 
+ 	$master_result = run_query($node_master, $query);
+ 	$standby_result = run_query($node_standby, $query);
+ 
+ 	is($master_result, $standby_result, "$test_name: query result matches");
+ }
+ 
+ # Setup master-standby configuration
+ sub setup_cluster
+ {
+ 	# Initialize master, data checksums are mandatory
+ 	$node_master = get_new_node('master');
+ 	$node_master->init;
+ 
+ 	# Custom parameters for master's postgresql.conf
+ 	$node_master->append_conf(
+ 		"postgresql.conf", qq(
+ wal_level = hot_standby
+ max_wal_senders = 2
+ shared_buffers = 16MB
+ hot_standby = on
+ autovacuum = off
+ full_page_writes = off
+ max_connections = 10
+ ));
+ 	# Start master
+ 	$node_master->start;
+ 
+ 	# Make standby
+ 	$node_standby = get_new_node('standby');
+ 	$node_master->backup('my_backup');
+ 	$node_standby->init_from_backup($node_master, 'my_backup');
+ 	my $connstr_master = $node_master->connstr('postgres');
+ 	$node_standby->append_conf(
+ 		"recovery.conf", qq(
+ primary_conninfo='$connstr_master'
+ standby_mode=on
+ ));
+ 
+ 	# Start standby
+ 	$node_standby->start;
+ }
+ 
+ # Clean up after the test. Stop both servers, if they're still running.
+ sub clean_setup
+ {
+ 	$node_master->teardown_node  if defined $node_master;
+ 	$node_standby->teardown_node if defined $node_standby;
+ }
+ 
+ 1;
diff --git a/contrib/bloom/blcost.c b/contrib/bloom/blcost.c
new file mode 100644
index ...d918dce
*** a/contrib/bloom/blcost.c
--- b/contrib/bloom/blcost.c
***************
*** 0 ****
--- 1,45 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blcost.c
+  *		Cost estimate function for bloom indexes.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blcost.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "fmgr.h"
+ #include "optimizer/cost.h"
+ #include "utils/selfuncs.h"
+ 
+ #include "bloom.h"
+ 
+ void
+ blcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
+ 			   Cost *indexStartupCost, Cost *indexTotalCost,
+ 			   Selectivity *indexSelectivity, double *indexCorrelation)
+ {
+ 	IndexOptInfo   *index = path->indexinfo;
+ 	List		   *qinfos;
+ 	GenericCosts	costs;
+ 
+ 	/* Do preliminary analysis of indexquals */
+ 	qinfos = deconstruct_indexquals(path);
+ 
+ 	MemSet(&costs, 0, sizeof(costs));
+ 
+ 	/* We have to visit all index tuples anyway */
+ 	costs.numIndexTuples = index->tuples;
+ 
+ 	/* Use generic estimate */
+ 	genericcostestimate(root, path, loop_count, qinfos, &costs);
+ 
+ 	*indexStartupCost = costs.indexStartupCost;
+ 	*indexTotalCost = costs.indexTotalCost;
+ 	*indexSelectivity = costs.indexSelectivity;
+ 	*indexCorrelation = costs.indexCorrelation;
+ }
diff --git a/contrib/bloom/blinsert.c b/contrib/bloom/blinsert.c
new file mode 100644
index ...15bff40
*** a/contrib/bloom/blinsert.c
--- b/contrib/bloom/blinsert.c
***************
*** 0 ****
--- 1,293 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blinsert.c
+  *		Bloom index build and insert functions.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blinsert.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "access/generic_xlog.h"
+ #include "catalog/index.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ 
+ #include "bloom.h"
+ 
+ PG_MODULE_MAGIC;
+ 
+ typedef struct
+ {
+ 	BloomState		blstate;
+ 	MemoryContext	tmpCtx;
+ 	char			data[BLCKSZ];
+ 	int64			count;
+ } BloomBuildState;
+ 
+ /*
+  * Flush page cached in BloomBuildState.
+  */
+ static void
+ flushCachedPage(Relation index, BloomBuildState *buildstate)
+ {
+ 	Page	page;
+ 	Buffer	buffer = BloomNewBuffer(index);
+ 
+ 	GenericXLogStart(index);
+ 	page = GenericXLogRegister(buffer, true);
+ 	memcpy(page, buildstate->data, BLCKSZ);
+ 	GenericXLogFinish();
+ 	UnlockReleaseBuffer(buffer);
+ }
+ 
+ /*
+  * (Re)initialize cached page in BloomBuildState.
+  */
+ static void
+ initCachedPage(BloomBuildState *buildstate)
+ {
+ 	memset(buildstate->data, 0, BLCKSZ);
+ 	BloomInitPage(buildstate->data, 0);
+ 	buildstate->count = 0;
+ }
+ 
+ /*
+  * Per-tuple callback from IndexBuildHeapScan.
+  */
+ static void
+ bloomBuildCallback(Relation index, HeapTuple htup, Datum *values,
+ 					bool *isnull, bool tupleIsAlive, void *state)
+ {
+ 	BloomBuildState	*buildstate = (BloomBuildState *)state;
+ 	MemoryContext	 oldCtx;
+ 	BloomTuple		*itup;
+ 
+ 	oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
+ 
+ 	itup = BloomFormTuple(&buildstate->blstate, &htup->t_self, values, isnull);
+ 
+ 	/* Try to add next item to cached page */
+ 	if (BloomPageAddItem(&buildstate->blstate, buildstate->data, itup) == false)
+ 	{
+ 		/* Cached page is full, flush it out and make a new one */
+ 		flushCachedPage(index, buildstate);
+ 
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		initCachedPage(buildstate);
+ 
+ 		if (BloomPageAddItem(&buildstate->blstate, buildstate->data, itup) == false)
+ 		{
+ 			/* We shouldn't be here since we're inserting to the empty page */
+ 			elog(ERROR, "can not add new tuple");
+ 		}
+ 	}
+ 	else
+ 	{
+ 		buildstate->count++;
+ 	}
+ 
+ 	MemoryContextSwitchTo(oldCtx);
+ 	MemoryContextReset(buildstate->tmpCtx);
+ }
+ 
+ IndexBuildResult *
+ blbuild(Relation heap, Relation index, IndexInfo *indexInfo)
+ {
+ 	IndexBuildResult   *result;
+ 	double				reltuples;
+ 	BloomBuildState		buildstate;
+ 
+ 	if (RelationGetNumberOfBlocks(index) != 0)
+ 		elog(ERROR, "index \"%s\" already contains data",
+ 			RelationGetRelationName(index));
+ 
+ 	/* Initialize the meta page */
+ 	BloomInitMetapage(index);
+ 
+ 	initBloomState(&buildstate.blstate, index);
+ 
+ 	buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
+ 												"Bloom build temporary context",
+ 												ALLOCSET_DEFAULT_MINSIZE,
+ 												ALLOCSET_DEFAULT_INITSIZE,
+ 												ALLOCSET_DEFAULT_MAXSIZE);
+ 
+ 	initCachedPage(&buildstate);
+ 
+ 	/* Do the heap scan */
+ 	reltuples = IndexBuildHeapScan(heap, index, indexInfo, true,
+ 									bloomBuildCallback, (void *) &buildstate);
+ 
+ 	if (buildstate.count > 0)
+ 		flushCachedPage(index, &buildstate);
+ 
+ 	MemoryContextDelete(buildstate.tmpCtx);
+ 
+ 	result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
+ 	result->heap_tuples = result->index_tuples = reltuples;
+ 
+ 	return result;
+ }
+ 
+ void
+ blbuildempty(Relation index)
+ {
+ 	if (RelationGetNumberOfBlocks(index) != 0)
+ 		elog(ERROR, "index \"%s\" already contains data",
+ 			RelationGetRelationName(index));
+ 
+ 	/* Initialize the meta page */
+ 	BloomInitMetapage(index);
+ }
+ 
+ bool
+ blinsert(Relation index, Datum *values, bool *isnull,
+ 		ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique)
+ {
+ 	BloomState			blstate;
+ 	BloomTuple		   *itup;
+ 	MemoryContext		oldCtx;
+ 	MemoryContext		insertCtx;
+ 	BloomMetaPageData  *metaData;
+ 	Buffer				buffer,
+ 						metaBuffer;
+ 	Page				page,
+ 						metaPage;
+ 	BlockNumber			blkno = InvalidBlockNumber;
+ 	OffsetNumber		nStart;
+ 
+ 	insertCtx = AllocSetContextCreate(CurrentMemoryContext,
+ 										"Bloom insert temporary context",
+ 										ALLOCSET_DEFAULT_MINSIZE,
+ 										ALLOCSET_DEFAULT_INITSIZE,
+ 										ALLOCSET_DEFAULT_MAXSIZE);
+ 
+ 	oldCtx = MemoryContextSwitchTo(insertCtx);
+ 
+ 	initBloomState(&blstate, index);
+ 	itup = BloomFormTuple(&blstate, ht_ctid, values, isnull);
+ 
+ 	/*
+ 	 * At first, try to insert new tuple to the first page in
+ 	 * notFullPage array.  If success we don't need to modify the
+ 	 * meta page.
+ 	 */
+ 	metaBuffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 	LockBuffer(metaBuffer, BUFFER_LOCK_SHARE);
+ 	metaData = BloomPageGetMeta(BufferGetPage(metaBuffer));
+ 
+ 	if (metaData->nEnd > metaData->nStart)
+ 	{
+ 		Page	page;
+ 
+ 		blkno = metaData->notFullPage[metaData->nStart];
+ 
+ 		Assert(blkno != InvalidBlockNumber);
+ 		LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		GenericXLogStart(index);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		if (BloomPageAddItem(&blstate, page, itup))
+ 		{
+ 			GenericXLogFinish();
+ 			UnlockReleaseBuffer(buffer);
+ 			ReleaseBuffer(metaBuffer);
+ 			goto away;
+ 		}
+ 		else
+ 		{
+ 			GenericXLogAbort();
+ 			UnlockReleaseBuffer(buffer);
+ 		}
+ 	}
+ 	else
+ 	{
+ 		/* First page in notFullPage isn't suitable */
+ 		LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
+ 	}
+ 
+ 	/*
+ 	 * Try other pages in notFullPage array.  We will have to change nStart
+ 	 * in metapage.  Thus, grab exclusive lock on metapage.
+ 	 */
+ 	LockBuffer(metaBuffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 	GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(metaBuffer, false);
+ 	metaData = BloomPageGetMeta(metaPage);
+ 
+ 	/*
+ 	 * Iterate over notFullPage array.  Skip page we already tried first.
+ 	 */
+ 	nStart = metaData->nStart;
+ 	if (metaData->nEnd > nStart &&
+ 		blkno == metaData->notFullPage[nStart] )
+ 		nStart++;
+ 
+ 	while (metaData->nEnd > nStart)
+ 	{
+ 		blkno = metaData->notFullPage[nStart];
+ 		Assert(blkno != InvalidBlockNumber);
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		if (BloomPageAddItem(&blstate, page, itup))
+ 		{
+ 			metaData->nStart = nStart;
+ 			GenericXLogFinish();
+ 			UnlockReleaseBuffer(buffer);
+ 			UnlockReleaseBuffer(metaBuffer);
+ 			goto away;
+ 		}
+ 		else
+ 		{
+ 			GenericXLogUnregister(buffer);
+ 			UnlockReleaseBuffer(buffer);
+ 		}
+ 		nStart++;
+ 	}
+ 
+ 	GenericXLogAbort();
+ 
+ 	/*
+ 	 * Didn't find place to insert in notFullPage array.  Allocate new page.
+ 	 */
+ 	buffer = BloomNewBuffer(index);
+ 
+ 	GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(metaBuffer, false);
+ 	metaData = BloomPageGetMeta(metaPage);
+ 	page = GenericXLogRegister(buffer, true);
+ 	BloomInitPage(page, 0);
+ 	BloomPageAddItem(&blstate, page, itup);
+ 
+ 	metaData->nStart = 0;
+ 	metaData->nEnd = 1;
+ 	metaData->notFullPage[ 0 ] = BufferGetBlockNumber(buffer);
+ 
+ 	GenericXLogFinish();
+ 
+ 	UnlockReleaseBuffer(buffer);
+ 	UnlockReleaseBuffer(metaBuffer);
+ 
+ away:
+ 	MemoryContextSwitchTo(oldCtx);
+ 	MemoryContextDelete(insertCtx);
+ 
+ 	return false;
+ }
diff --git a/contrib/bloom/bloom--1.0.sql b/contrib/bloom/bloom--1.0.sql
new file mode 100644
index ...7fa7513
*** a/contrib/bloom/bloom--1.0.sql
--- b/contrib/bloom/bloom--1.0.sql
***************
*** 0 ****
--- 1,19 ----
+ CREATE OR REPLACE FUNCTION blhandler(internal)
+ RETURNS index_am_handler
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C;
+ 
+ -- Access method
+ CREATE ACCESS METHOD bloom TYPE INDEX HANDLER blhandler;
+ 
+ -- Opclasses
+ 
+ CREATE OPERATOR CLASS int4_ops
+ DEFAULT FOR TYPE int4 USING bloom AS
+ 	OPERATOR	1	=(int4, int4),
+ 	FUNCTION	1	hashint4(int4);
+ 
+ CREATE OPERATOR CLASS text_ops
+ DEFAULT FOR TYPE text USING bloom AS
+ 	OPERATOR	1	=(text, text),
+ 	FUNCTION	1	hashtext(text);
diff --git a/contrib/bloom/bloom.control b/contrib/bloom/bloom.control
new file mode 100644
index ...4d4124b
*** a/contrib/bloom/bloom.control
--- b/contrib/bloom/bloom.control
***************
*** 0 ****
--- 1,5 ----
+ # bloom extension
+ comment = 'bloom access method - signature file based index'
+ default_version = '1.0'
+ module_pathname = '$libdir/bloom'
+ relocatable = true
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
new file mode 100644
index ...6f20ee4
*** a/contrib/bloom/bloom.h
--- b/contrib/bloom/bloom.h
***************
*** 0 ****
--- 1,176 ----
+ /*-------------------------------------------------------------------------
+  *
+  * bloom.h
+  *	  Header for bloom index.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/bloom.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef _BLOOM_H_
+ #define _BLOOM_H_
+ 
+ #include "access/amapi.h"
+ #include "access/generic_xlog.h"
+ #include "access/itup.h"
+ #include "access/xlog.h"
+ #include "nodes/relation.h"
+ #include "fmgr.h"
+ 
+ /* Support procedures numbers */
+ #define	BLOOM_HASH_PROC			1
+ #define	BLOOM_NPROC				1
+ 
+ /* Scan strategies */
+ #define	BLOOM_EQUAL_STRATEGY	1
+ #define	BLOOM_NSTRATEGIES		1
+ 
+ /* Opaque for bloom pages */
+ typedef struct BloomPageOpaqueData
+ {
+ 	OffsetNumber	maxoff;
+ 	uint16			flags;
+ } BloomPageOpaqueData;
+ 
+ typedef BloomPageOpaqueData *BloomPageOpaque;
+ 
+ /* Bloom page flags */
+ #define BLOOM_META		(1<<0)
+ #define BLOOM_DELETED	(2<<0)
+ 
+ #define BloomPageGetOpaque(page) ((BloomPageOpaque) PageGetSpecialPointer(page))
+ #define BloomPageGetMaxOffset(page) (BloomPageGetOpaque(page)->maxoff)
+ #define BloomPageIsMeta(page) (BloomPageGetOpaque(page)->flags & BLOOM_META)
+ #define BloomPageIsDeleted(page) (BloomPageGetOpaque(page)->flags & BLOOM_DELETED)
+ #define BloomPageSetDeleted(page) (BloomPageGetOpaque(page)->flags |= BLOOM_DELETED)
+ #define BloomPageSetNonDeleted(page) (BloomPageGetOpaque(page)->flags &= ~BLOOM_DELETED)
+ #define BloomPageGetData(page)		((BloomTuple *)PageGetContents(page))
+ #define BloomPageGetTuple(state, page, offset) \
+ 	((BloomTuple *)(PageGetContents(page) \
+ 		+ (state)->sizeOfBloomTuple * ((offset) - 1)))
+ #define BloomPageGetNextTuple(state, tuple) \
+ 	((BloomTuple *)((Pointer)(tuple) + (state)->sizeOfBloomTuple))
+ 
+ /* Preserved page numbers */
+ #define BLOOM_METAPAGE_BLKNO	(0)
+ #define BLOOM_HEAD_BLKNO		(1) /* first data page */
+ 
+ /* Bloom index options */
+ typedef struct BloomOptions
+ {
+ 	int32	vl_len_;				 /* varlena header (do not touch directly!) */
+ 	int		bloomLength;			 /* length of signature in uint16 */
+ 	int		bitSize[INDEX_MAX_KEYS]; /* signature bits per index key */
+ } BloomOptions;
+ 
+ /*
+  * FreeBlockNumberArray - array of block numbers sized so that metadata fill
+  * all space in metapage.
+  */
+ typedef BlockNumber FreeBlockNumberArray[
+ 	MAXALIGN_DOWN(
+ 		BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(BloomPageOpaqueData))
+ 			   - MAXALIGN(sizeof(uint16) * 2 + sizeof(uint32) + sizeof(BloomOptions))
+ 	) / sizeof(BlockNumber) 
+ ];
+ 
+ /* Metadata of bloom index */
+ typedef struct BloomMetaPageData
+ {
+ 	uint32					magickNumber;
+ 	uint16					nStart;
+ 	uint16					nEnd;
+ 	BloomOptions			opts;
+ 	FreeBlockNumberArray	notFullPage;
+ } BloomMetaPageData;
+ 
+ /* Magic number to distinguish bloom pages among anothers */
+ #define BLOOM_MAGICK_NUMBER	(0xDBAC0DED)
+ 
+ /* Number of blocks numbers fit in BloomMetaPageData */
+ #define BloomMetaBlockN		(sizeof(FreeBlockNumberArray) / sizeof(BlockNumber))
+ 
+ #define BloomPageGetMeta(page)	((BloomMetaPageData *) PageGetContents(page))
+ 
+ typedef struct BloomState 
+ {
+ 	FmgrInfo			hashFn[INDEX_MAX_KEYS];
+ 	BloomOptions	   *opts; /* stored in rd_amcache and defined at creation time */
+ 	int32				nColumns;
+ 	/* 
+ 	 * sizeOfBloomTuple is index's specific, and it depends on
+ 	 * reloptions, so precompute it
+ 	 */
+ 	int32				sizeOfBloomTuple; 
+ } BloomState;
+ 
+ #define BloomPageGetFreeSpace(state, page) \
+ 	(BLCKSZ - MAXALIGN(SizeOfPageHeaderData) \
+ 		- BloomPageGetMaxOffset(page) * (state)->sizeOfBloomTuple \
+ 		- MAXALIGN(sizeof(BloomPageOpaqueData)))
+ 
+ /*
+  * Tuples are very different from all other relations
+  */
+ typedef uint16	SignType;
+ 
+ typedef struct BloomTuple
+ {
+ 	ItemPointerData		heapPtr;
+ 	SignType			sign[1];
+ } BloomTuple;
+ 
+ #define BLOOMTUPLEHDRSZ	offsetof(BloomTuple, sign)
+ 
+ /* Opaque data structure for bloom index scan */
+ typedef struct BloomScanOpaqueData
+ {
+ 	SignType   *sign;	/* Scan signature */
+ 	BloomState	state;
+ } BloomScanOpaqueData;
+ 
+ typedef BloomScanOpaqueData *BloomScanOpaque;
+ 
+ /* blutils.c */
+ extern void _PG_init(void);
+ extern Datum blhandler(PG_FUNCTION_ARGS);
+ extern void initBloomState(BloomState *state, Relation index);
+ extern void BloomInitMetapage(Relation index);
+ extern void BloomInitPage(Page page, uint16 flags);
+ extern Buffer BloomNewBuffer(Relation index);
+ extern void signValue(BloomState *state, SignType *sign, Datum value, int attno);
+ extern BloomTuple* BloomFormTuple(BloomState *state, ItemPointer iptr, Datum *values, bool *isnull);
+ extern bool BloomPageAddItem(BloomState *state, Page page, BloomTuple *tuple);
+ 
+ /* blvalidate.c */
+ extern bool blvalidate(Oid opclassoid);
+ 
+ /* interface functions */
+ extern bool blinsert(Relation index, Datum *values, bool *isnull,
+ 					 ItemPointer ht_ctid, Relation heapRel,
+ 					 IndexUniqueCheck checkUnique);
+ extern IndexScanDesc blbeginscan(Relation r, int nkeys, int norderbys);
+ extern int64 blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
+ extern void blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ 					 ScanKey orderbys, int norderbys);
+ extern void blendscan(IndexScanDesc scan);
+ extern void blmarkpos(IndexScanDesc scan);
+ extern void blrestrpos(IndexScanDesc scan);
+ extern IndexBuildResult *blbuild(Relation heap, Relation index,
+ 								   struct IndexInfo *indexInfo);
+ extern void blbuildempty(Relation index);
+ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
+ 	IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback,
+ 	void *callback_state);
+ extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
+ 	IndexBulkDeleteResult *stats);
+ extern bytea *bloptions(Datum reloptions, bool validate);
+ extern void blcostestimate(PlannerInfo *root, IndexPath *path,
+ 						   double loop_count, Cost *indexStartupCost,
+ 						   Cost *indexTotalCost, Selectivity *indexSelectivity,
+ 						   double *indexCorrelation);
+ 
+ #endif
diff --git a/contrib/bloom/blscan.c b/contrib/bloom/blscan.c
new file mode 100644
index ...467a1b1
*** a/contrib/bloom/blscan.c
--- b/contrib/bloom/blscan.c
***************
*** 0 ****
--- 1,174 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blscan.c
+  *		Bloom index scan functions.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blscan.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/relscan.h"
+ #include "pgstat.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/lmgr.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ 
+ #include "bloom.h"
+ 
+ IndexScanDesc
+ blbeginscan(Relation r, int nkeys, int norderbys)
+ {
+ 	IndexScanDesc scan;
+ 
+ 	scan = RelationGetIndexScan(r, nkeys, norderbys);
+ 
+ 	return scan;
+ }
+ 
+ void
+ blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ 							 ScanKey orderbys, int norderbys)
+ {
+ 	BloomScanOpaque so;
+ 
+ 	so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so == NULL)
+ 	{
+ 		/* if called from blbeginscan */
+ 		so = (BloomScanOpaque) palloc(sizeof(BloomScanOpaqueData));
+ 		initBloomState(&so->state, scan->indexRelation);
+ 		scan->opaque = so;
+ 
+ 	}
+ 	else
+ 	{
+ 		if (so->sign)
+ 			pfree(so->sign);
+ 	}
+ 	so->sign = NULL;
+ 
+ 	if (scankey && scan->numberOfKeys > 0)
+ 	{
+ 		memmove(scan->keyData, scankey,
+ 				scan->numberOfKeys * sizeof(ScanKeyData));
+ 	}
+ }
+ 
+ void
+ blendscan(IndexScanDesc scan)
+ {
+ 	BloomScanOpaque so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so->sign)
+ 		pfree(so->sign);
+ 	so->sign = NULL;
+ }
+ 
+ void
+ blmarkpos(IndexScanDesc scan)
+ {
+ 	elog(ERROR, "Bloom does not support mark/restore");
+ }
+ 
+ void
+ blrestrpos(IndexScanDesc scan)
+ {
+ 	elog(ERROR, "Bloom does not support mark/restore");
+ }
+ 
+ int64
+ blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
+ {
+ 	int64					ntids = 0;
+ 	BlockNumber				blkno = BLOOM_HEAD_BLKNO,
+ 							npages;
+ 	int						i;
+ 	BufferAccessStrategy	bas;
+ 	BloomScanOpaque 		so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so->sign == NULL && scan->numberOfKeys > 0)
+ 	{
+ 		/* New search: have to calculate search signature */
+ 		ScanKey skey = scan->keyData;
+ 
+ 		so->sign = palloc0(sizeof(SignType) * so->state.opts->bloomLength); 
+ 		
+ 		for(i = 0; i < scan->numberOfKeys; i++)
+ 		{
+ 			/*
+ 			 * Assume bloom-indexable operators to be strict, so nothing
+ 			 * could be found for NULL key.
+ 			 */
+ 			if (skey->sk_flags & SK_ISNULL)
+ 			{
+ 				pfree(so->sign);
+ 				so->sign = NULL;
+ 				return 0;
+ 			}
+ 
+ 			/* Add next value to the signature */
+ 			signValue(&so->state, so->sign, skey->sk_argument,
+ 					  skey->sk_attno - 1);
+ 
+ 			skey++;
+ 		}
+ 	}
+ 
+ 	/*
+ 	 * We're going to read the whole index. This is why we use appropriate
+ 	 * buffer access strategy.
+ 	 */
+ 	bas = GetAccessStrategy(BAS_BULKREAD);
+ 	npages = RelationGetNumberOfBlocks(scan->indexRelation);
+ 
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		Buffer 			buffer;
+ 		Page			page;
+ 
+ 		buffer = ReadBufferExtended(scan->indexRelation, MAIN_FORKNUM,
+ 									blkno, RBM_NORMAL, bas);
+ 
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 		page = BufferGetPage(buffer);
+ 
+ 		if (!BloomPageIsDeleted(page))
+ 		{
+ 			OffsetNumber offset, maxOffset = BloomPageGetMaxOffset(page);
+ 
+ 			for (offset = 1; offset <= maxOffset; offset++)
+ 			{
+ 				BloomTuple *itup = BloomPageGetTuple(&so->state, page, offset);
+ 				bool		res = true;
+ 
+ 				/* Check index signature with scan signature */
+ 				for (i = 0; res && i < so->state.opts->bloomLength; i++)
+ 				{
+ 					if ((itup->sign[i] & so->sign[i]) != so->sign[i])
+ 						res = false;
+ 				}
+ 
+ 				/* Add matching tuples to bitmap */
+ 				if (res)
+ 				{
+ 					tbm_add_tuples(tbm, &itup->heapPtr, 1, true);
+ 					ntids++;
+ 				}
+ 			}
+ 		}
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 		CHECK_FOR_INTERRUPTS();
+ 	}
+ 	FreeAccessStrategy(bas);
+ 
+ 	return ntids;
+ }
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
new file mode 100644
index ...a7ec0c5
*** a/contrib/bloom/blutils.c
--- b/contrib/bloom/blutils.c
***************
*** 0 ****
--- 1,390 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blutils.c
+  *		Bloom index utilities.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blutils.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/amapi.h"
+ #include "access/generic_xlog.h"
+ #include "catalog/index.h"
+ #include "storage/lmgr.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "utils/memutils.h"
+ #include "access/reloptions.h"
+ #include "storage/freespace.h"
+ #include "storage/indexfsm.h"
+ 
+ #include "bloom.h"
+ 
+ /* Signature dealing macros */
+ #define BITSIGNTYPE	(BITS_PER_BYTE * sizeof(SignType))
+ #define GETWORD(x,i) ( *( (SignType*)(x) + (int)( (i) / BITSIGNTYPE ) ) )
+ #define CLRBIT(x,i)   GETWORD(x,i) &= ~( 0x01 << ( (i) % BITSIGNTYPE ) )
+ #define SETBIT(x,i)   GETWORD(x,i) |=  ( 0x01 << ( (i) % BITSIGNTYPE ) )
+ #define GETBIT(x,i) ( (GETWORD(x,i) >> ( (i) % BITSIGNTYPE )) & 0x01 )
+ 
+ PG_FUNCTION_INFO_V1(blhandler);
+ 
+ static relopt_kind bl_relopt_kind;
+ 
+ /*
+  * Module initialize function: initilized relation options.
+  */
+ void 
+ _PG_init(void)
+ {
+ 	int		i;
+ 	char	buf[16];
+ 
+ 	bl_relopt_kind = add_reloption_kind();
+ 
+ 	add_int_reloption(bl_relopt_kind, "length",
+ 					  "Length of signature in uint16 type", 5, 1, 256);
+ 
+ 	for(i = 0; i < INDEX_MAX_KEYS;i ++)
+ 	{
+ 		snprintf(buf, 16, "col%d", i+1);
+ 		add_int_reloption(bl_relopt_kind, buf,
+ 						  "Number of bits for corresponding column", 2, 1, 2048);
+ 	}
+ }
+ 
+ /*
+  * Bloom handler function: return IndexAmRoutine with access method parameters
+  * and callbacks.
+  */
+ Datum
+ blhandler(PG_FUNCTION_ARGS)
+ {
+ 	IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
+ 
+ 	amroutine->amstrategies = 1;
+ 	amroutine->amsupport = 1;
+ 	amroutine->amcanorder = false;
+ 	amroutine->amcanorderbyop = false;
+ 	amroutine->amcanbackward = false;
+ 	amroutine->amcanunique = false;
+ 	amroutine->amcanmulticol = true;
+ 	amroutine->amoptionalkey = true;
+ 	amroutine->amsearcharray = false;
+ 	amroutine->amsearchnulls = false;
+ 	amroutine->amstorage = false;
+ 	amroutine->amclusterable = false;
+ 	amroutine->ampredlocks = false;
+ 	amroutine->amkeytype = 0;
+ 
+ 	amroutine->aminsert = blinsert;
+ 	amroutine->ambeginscan = blbeginscan;
+ 	amroutine->amgettuple = NULL;
+ 	amroutine->amgetbitmap = blgetbitmap;
+ 	amroutine->amrescan = blrescan;
+ 	amroutine->amendscan = blendscan;
+ 	amroutine->ammarkpos = blmarkpos;
+ 	amroutine->amrestrpos = blrestrpos;
+ 	amroutine->ambuild = blbuild;
+ 	amroutine->ambuildempty = blbuildempty;
+ 	amroutine->ambulkdelete = blbulkdelete;
+ 	amroutine->amvacuumcleanup = blvacuumcleanup;
+ 	amroutine->amcanreturn = NULL;
+ 	amroutine->amcostestimate = blcostestimate;
+ 	amroutine->amoptions = bloptions;
+ 	amroutine->amvalidate = blvalidate;
+ 
+ 	PG_RETURN_POINTER(amroutine);
+ }
+ 
+ void 
+ initBloomState(BloomState *state, Relation index)
+ {
+ 	int	i;
+ 
+ 	state->nColumns = index->rd_att->natts;
+ 
+ 	/* Initialize hash function for each attribute */
+ 	for (i = 0; i < index->rd_att->natts; i++)
+ 	{
+ 		fmgr_info_copy(&(state->hashFn[i]),
+ 						index_getprocinfo(index, i + 1, BLOOM_HASH_PROC),
+ 						CurrentMemoryContext);
+ 	}
+ 
+ 	/* Inititalize amcache if needed */
+ 	if (!index->rd_amcache)
+ 	{
+ 		Buffer				buffer;
+ 		Page				page;
+ 		BloomMetaPageData	*meta;
+ 		BloomOptions		*opts;
+ 
+ 		opts = MemoryContextAlloc(index->rd_indexcxt, sizeof(BloomOptions));
+ 
+ 		buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 
+ 		page = BufferGetPage(buffer);
+ 
+ 		if (!BloomPageIsMeta(page))
+ 			elog(ERROR, "Relation is not a bloom index");
+ 		meta = BloomPageGetMeta(BufferGetPage(buffer));
+ 
+ 		if (meta->magickNumber != BLOOM_MAGICK_NUMBER)
+ 			elog(ERROR, "Relation is not a bloom index");
+ 
+ 		*opts = meta->opts;
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 
+ 		index->rd_amcache = (void *)opts;
+ 	}
+ 
+ 	state->opts = (BloomOptions *)index->rd_amcache;
+ 	state->sizeOfBloomTuple = BLOOMTUPLEHDRSZ +
+ 									sizeof(SignType) * state->opts->bloomLength; 
+ }
+ 
+ /*
+  * Add bits of given value to the signature.
+  */
+ void
+ signValue(BloomState *state, SignType *sign, Datum value, int attno)
+ {
+ 	uint32		hashVal;
+ 	int 		nBit, j;
+ 
+ 	/*
+ 	 * init generator with "column's" number to get
+ 	 * "hashed" seed for new value. We don't want to map
+ 	 * the same numbers from different columns into the same bits!
+ 	 */
+ 	srand(attno);
+ 
+ 	/*
+ 	 * Init hash sequence to map our value into bits. the same values
+ 	 * in different columns will be mapped into different bits because
+ 	 * of step above
+ 	 */
+ 	hashVal = DatumGetInt32(FunctionCall1(&state->hashFn[attno], value));
+ 	srand(hashVal ^ rand());
+ 
+ 	for (j = 0; j < state->opts->bitSize[attno]; j++)
+ 	{
+ 		/* prevent mutiple evaluation */
+ 		nBit = rand() % (state->opts->bloomLength * BITSIGNTYPE); 
+ 		SETBIT(sign, nBit);
+ 	}
+ }
+ 
+ /*
+  * Make bloom tuple from values.
+  */
+ BloomTuple *
+ BloomFormTuple(BloomState *state, ItemPointer iptr, Datum *values, bool *isnull)
+ {
+ 	int 		i;
+ 	BloomTuple *res = (BloomTuple *)palloc0(state->sizeOfBloomTuple);
+ 
+ 	res->heapPtr = *iptr;
+ 
+     /*
+ 	 * Blooming
+ 	 */
+ 	for (i = 0; i < state->nColumns; i++)
+ 	{
+ 		/* skip nulls */
+ 		if (isnull[i])
+ 			continue;
+ 
+ 		signValue(state, res->sign, values[i], i);
+ 	}
+ 
+ 	return res;
+ }
+ 
+ /*
+  * Add new bloom tuple to the page.
+  */
+ bool
+ BloomPageAddItem(BloomState *state, Page page, BloomTuple *tuple)
+ {
+ 	BloomTuple		   *itup;
+ 	BloomPageOpaque		opaque;
+ 	Pointer				ptr;
+ 
+ 	if (BloomPageGetFreeSpace(state, page) < state->sizeOfBloomTuple)
+ 		return false;
+ 
+ 	/* Copy new tuple to the end of page */
+ 	opaque = BloomPageGetOpaque(page);
+ 	itup = BloomPageGetTuple(state, page, opaque->maxoff + 1);
+ 	memcpy((Pointer)itup, (Pointer)tuple, state->sizeOfBloomTuple);
+ 
+ 	/* Adjust maxoff and pd_lower */
+ 	opaque->maxoff++;
+ 	ptr = (Pointer)BloomPageGetTuple(state, page, opaque->maxoff + 1);
+ 	((PageHeader) page)->pd_lower = ptr - page;
+ 
+ 	return true;
+ }
+ 
+ /*
+  * Allocate a new page (either by recycling, or by extending the index file)
+  * The returned buffer is already pinned and exclusive-locked
+  * Caller is responsible for initializing the page by calling BloomInitBuffer
+  */
+ Buffer
+ BloomNewBuffer(Relation index)
+ {
+ 	Buffer      buffer;
+ 	bool        needLock;
+ 
+ 	/* First, try to get a page from FSM */
+ 	for (;;)
+ 	{
+ 		BlockNumber blkno = GetFreeIndexPage(index);
+ 
+ 		if (blkno == InvalidBlockNumber)
+ 			break;
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 
+ 		/*
+ 		 * We have to guard against the possibility that someone else already
+ 		 * recycled this page; the buffer may be locked if so.
+ 		 */
+ 		if (ConditionalLockBuffer(buffer))
+ 		{
+ 			Page	page = BufferGetPage(buffer);
+ 
+ 			if (PageIsNew(page))
+ 				return buffer;  /* OK to use, if never initialized */
+ 
+ 			if (BloomPageIsDeleted(page))
+ 				return buffer;  /* OK to use */
+ 
+ 			LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ 		}
+ 
+ 		/* Can't use it, so release buffer and try again */
+ 		ReleaseBuffer(buffer);
+ 	}
+ 
+ 	/* Must extend the file */
+ 	needLock = !RELATION_IS_LOCAL(index);
+ 	if (needLock)
+ 		LockRelationForExtension(index, ExclusiveLock);
+ 
+ 	buffer = ReadBuffer(index, P_NEW);
+ 	LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 	if (needLock)
+ 		UnlockRelationForExtension(index, ExclusiveLock);
+ 
+ 	return buffer;
+ }
+ 
+ /*
+  * Initialize bloom page.
+  */
+ void
+ BloomInitPage(Page page, uint16 flags)
+ {
+ 	BloomPageOpaque opaque;
+ 
+ 	PageInit(page, BLCKSZ, sizeof(BloomPageOpaqueData));
+ 
+ 	opaque = BloomPageGetOpaque(page);
+ 	memset(opaque, 0, sizeof(BloomPageOpaqueData));
+ 	opaque->maxoff = 0;
+ 	opaque->flags = flags;
+ }
+ 
+ static BloomOptions *
+ makeDefaultBloomOptions(BloomOptions *opts)
+ {
+ 	int i;
+ 
+ 	if (!opts)
+ 		opts = palloc0(sizeof(BloomOptions));
+ 
+ 	if (opts->bloomLength <= 0)
+ 		opts->bloomLength = 5;
+ 
+ 	for(i = 0; i < INDEX_MAX_KEYS; i++)
+ 		if (opts->bitSize[i] <= 0
+ 				|| opts->bitSize[i] >= opts->bloomLength * sizeof(SignType))
+ 			opts->bitSize[i] = 2;
+ 
+ 	return opts;
+ }
+ 
+ /*
+  * Initialize metapage for bloom index.
+  */
+ void
+ BloomInitMetapage(Relation index)
+ {
+ 	Page				metaPage;
+ 	Buffer				metaBuffer;
+ 	BloomMetaPageData  *metadata;
+ 
+ 	metaBuffer = BloomNewBuffer(index);
+ 	Assert(BufferGetBlockNumber(metaBuffer) == BLOOM_METAPAGE_BLKNO);
+ 
+ 	GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(metaBuffer, true);
+ 
+ 	BloomInitPage(metaPage, BLOOM_META);
+ 	metadata = BloomPageGetMeta(metaPage);
+ 	memset(metadata, 0, sizeof(BloomMetaPageData));
+ 	metadata->magickNumber = BLOOM_MAGICK_NUMBER;
+ 	metadata->opts = *makeDefaultBloomOptions((BloomOptions*)index->rd_options);
+ 	((PageHeader) metaPage)->pd_lower += sizeof(BloomMetaPageData);
+ 
+ 	GenericXLogFinish();
+ 	UnlockReleaseBuffer(metaBuffer);
+ }
+ 
+ /*
+  * Initialize options for bloom index.
+  */
+ bytea *
+ bloptions(Datum reloptions, bool validate)
+ {
+ 	relopt_value	   *options;
+ 	int					numoptions;
+ 	BloomOptions	   *rdopts;
+ 	relopt_parse_elt	tab[INDEX_MAX_KEYS + 1];
+ 	int					i;
+ 	char				buf[16];
+ 
+ 	tab[0].optname = "length";
+ 	tab[0].opttype = RELOPT_TYPE_INT;
+ 	tab[0].offset = offsetof(BloomOptions, bloomLength);
+ 
+ 	for(i = 0; i < INDEX_MAX_KEYS; i++)
+ 	{
+ 		snprintf(buf, sizeof(buf), "col%d", i + 1);
+ 		tab[i + 1].optname = pstrdup(buf);
+ 		tab[i + 1].opttype = RELOPT_TYPE_INT;
+ 		tab[i + 1].offset = offsetof(BloomOptions, bitSize[i]);
+ 	}
+ 
+ 	options = parseRelOptions(reloptions, validate, bl_relopt_kind, &numoptions);
+ 	rdopts = allocateReloptStruct(sizeof(BloomOptions), options, numoptions);
+ 	fillRelOptions((void *) rdopts, sizeof(BloomOptions), options, numoptions,
+ 						validate, tab, INDEX_MAX_KEYS + 1);
+ 		
+ 	rdopts = makeDefaultBloomOptions(rdopts);
+ 
+ 	return (bytea *)rdopts;
+ }
diff --git a/contrib/bloom/blvacuum.c b/contrib/bloom/blvacuum.c
new file mode 100644
index ...c694714
*** a/contrib/bloom/blvacuum.c
--- b/contrib/bloom/blvacuum.c
***************
*** 0 ****
--- 1,195 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blvacuum.c
+  *		Bloom VACUUM functions.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blvacuum.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "catalog/storage.h"
+ #include "commands/vacuum.h"
+ #include "miscadmin.h"
+ #include "postmaster/autovacuum.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "storage/lmgr.h"
+ 
+ #include "bloom.h"
+ 
+ IndexBulkDeleteResult *
+ blbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
+ 			 IndexBulkDeleteCallback callback, void *callback_state)
+ {
+ 	Relation				index = info->index;
+ 	BlockNumber				blkno,
+ 							npages;
+ 	FreeBlockNumberArray	notFullPage;
+ 	int						countPage = 0;
+ 	BloomState				state;
+ 	Buffer					buffer;
+ 	Page					page;
+ 
+ 	if (stats == NULL)
+ 		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+ 
+ 	initBloomState(&state, index); 
+ 
+ 	/*
+ 	 * Interate over the pages. We don't care about concurrently added pages,
+ 	 * they can't contain tuples to delete.
+ 	 */
+ 	npages = RelationGetNumberOfBlocks(index);
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		BloomTuple *itup, *itupPtr, *itupEnd;
+ 
+ 		buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
+ 									RBM_NORMAL, info->strategy);
+ 
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		GenericXLogStart(index);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		if (BloomPageIsDeleted(page))
+ 		{
+ 			UnlockReleaseBuffer(buffer);
+ 			CHECK_FOR_INTERRUPTS();
+ 			continue;
+ 		}
+ 
+ 		/* Iterate over the tuples */
+ 		itup = BloomPageGetTuple(&state, page, 1);
+ 		itupPtr = BloomPageGetTuple(&state, page, 1);
+ 		itupEnd = BloomPageGetTuple(&state, page, BloomPageGetMaxOffset(page) + 1);
+ 		while (itup < itupEnd)
+ 		{
+ 			/* Do we have to delete this tuple? */
+ 			if (callback(&itup->heapPtr, callback_state))
+ 			{
+ 				stats->tuples_removed += 1;
+ 				BloomPageGetOpaque(page)->maxoff--;
+ 			} 
+ 			else 
+ 			{
+ 				if (itupPtr != itup)
+ 				{
+ 					/*
+ 					 * If we already delete something before, we have to move
+ 					 * this tuple backward.
+ 					 */
+ 					memmove((Pointer)itupPtr, (Pointer)itup,
+ 							state.sizeOfBloomTuple);
+ 				}
+ 				stats->num_index_tuples++;
+ 				itupPtr = BloomPageGetNextTuple(&state, itupPtr);
+ 			}
+ 
+ 			itup = BloomPageGetNextTuple(&state, itup);
+ 		}
+ 
+ 		/* Did we delete something? */
+ 		if (itupPtr != itup)
+ 		{
+ 			/* Is it empty page now? */
+ 			if (itupPtr == BloomPageGetData(page))
+ 				BloomPageSetDeleted(page);
+ 			/* Adjust pg_lower */
+ 			((PageHeader) page)->pd_lower = (Pointer)itupPtr - page;
+ 			/* Finish WAL-logging */
+ 			GenericXLogFinish();
+ 		}
+ 		else
+ 		{
+ 			/* Didn't change anything: abort WAL-logging */
+ 			GenericXLogAbort();
+ 		}
+ 
+ 		if (!BloomPageIsDeleted(page) && 
+ 				BloomPageGetFreeSpace(&state, page) > state.sizeOfBloomTuple && 
+ 				countPage < BloomMetaBlockN)
+ 			notFullPage[countPage++] = blkno;
+ 		UnlockReleaseBuffer(buffer);
+ 		CHECK_FOR_INTERRUPTS();
+ 	}
+ 
+ 	if (countPage > 0)
+ 	{
+ 		BloomMetaPageData	*metaData;
+ 
+ 		buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 		GenericXLogStart(index);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		metaData = BloomPageGetMeta(page);
+ 		memcpy(metaData->notFullPage, notFullPage, sizeof(FreeBlockNumberArray));
+ 		metaData->nStart=0;
+ 		metaData->nEnd = countPage;
+ 
+ 		GenericXLogFinish();
+ 		UnlockReleaseBuffer(buffer);
+ 	}
+ 
+ 	return stats;
+ }
+ 
+ IndexBulkDeleteResult *
+ blvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
+ {
+ 	Relation	index = info->index;
+ 	BlockNumber npages,
+ 				blkno;
+ 	BlockNumber	totFreePages;
+ 
+ 	if (info->analyze_only)
+ 		return stats;
+ 
+ 	if (stats == NULL)
+ 		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+ 
+ 	/*
+ 	 * Iterate over the pages: insert deleted pages into FSM and collect
+ 	 * statistics.
+ 	 */
+ 	npages = RelationGetNumberOfBlocks(index);
+ 	totFreePages = 0;
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		Buffer		buffer;
+ 		Page		page;
+ 
+ 		vacuum_delay_point();
+ 
+ 		buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
+ 									RBM_NORMAL, info->strategy);
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 		page = (Page) BufferGetPage(buffer);
+ 
+ 		if (BloomPageIsDeleted(page))
+ 		{
+ 			RecordFreeIndexPage(index, blkno);
+ 			totFreePages++;
+ 		}
+ 		else
+ 		{
+ 			stats->num_index_tuples += BloomPageGetMaxOffset(page);
+ 			stats->estimated_count += BloomPageGetMaxOffset(page);
+ 		}
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 	}
+ 
+ 	IndexFreeSpaceMapVacuum(info->index);
+ 	stats->pages_free = totFreePages;
+ 	stats->num_pages = RelationGetNumberOfBlocks(index);
+ 
+ 	return stats;
+ }
diff --git a/contrib/bloom/blvalidate.c b/contrib/bloom/blvalidate.c
new file mode 100644
index ...5344b81
*** a/contrib/bloom/blvalidate.c
--- b/contrib/bloom/blvalidate.c
***************
*** 0 ****
--- 1,220 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blvalidate.c
+  *	  Opclass validator for bloom.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blvalidate.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/amvalidate.h"
+ #include "access/htup_details.h"
+ #include "catalog/pg_amop.h"
+ #include "catalog/pg_amproc.h"
+ #include "catalog/pg_opclass.h"
+ #include "catalog/pg_opfamily.h"
+ #include "catalog/pg_type.h"
+ #include "utils/builtins.h"
+ #include "utils/lsyscache.h"
+ #include "utils/syscache.h"
+ 
+ #include "bloom.h"
+ 
+ /*
+  * Validator for a bloom opclass.
+  */
+ bool
+ blvalidate(Oid opclassoid)
+ {
+ 	bool		result = true;
+ 	HeapTuple	classtup;
+ 	Form_pg_opclass classform;
+ 	Oid			opfamilyoid;
+ 	Oid			opcintype;
+ 	Oid			opckeytype;
+ 	char	   *opclassname;
+ 	HeapTuple	familytup;
+ 	Form_pg_opfamily familyform;
+ 	char	   *opfamilyname;
+ 	CatCList   *proclist,
+ 			   *oprlist;
+ 	List	   *grouplist;
+ 	OpFamilyOpFuncGroup *opclassgroup;
+ 	int			i;
+ 	ListCell   *lc;
+ 
+ 	/* Fetch opclass information */
+ 	classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
+ 	if (!HeapTupleIsValid(classtup))
+ 		elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
+ 	classform = (Form_pg_opclass) GETSTRUCT(classtup);
+ 
+ 	opfamilyoid = classform->opcfamily;
+ 	opcintype = classform->opcintype;
+ 	opckeytype = classform->opckeytype;
+ 	if (!OidIsValid(opckeytype))
+ 		opckeytype = opcintype;
+ 	opclassname = NameStr(classform->opcname);
+ 
+ 	/* Fetch opfamily information */
+ 	familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
+ 	if (!HeapTupleIsValid(familytup))
+ 		elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
+ 	familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
+ 
+ 	opfamilyname = NameStr(familyform->opfname);
+ 
+ 	/* Fetch all operators and support functions of the opfamily */
+ 	oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
+ 	proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
+ 
+ 	/* Check individual support functions */
+ 	for (i = 0; i < proclist->n_members; i++)
+ 	{
+ 		HeapTuple	proctup = &proclist->members[i]->tuple;
+ 		Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
+ 		bool		ok;
+ 
+ 		/*
+ 		 * All bloom support functions should be registered with matching
+ 		 * left/right types
+ 		 */
+ 		if (procform->amproclefttype != procform->amprocrighttype)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains support procedure %s with cross-type registration",
+ 							opfamilyname,
+ 							format_procedure(procform->amproc))));
+ 			result = false;
+ 		}
+ 
+ 		/*
+ 		 * We can't check signatures except within the specific opclass, since
+ 		 * we need to know the associated opckeytype in many cases.
+ 		 */
+ 		if (procform->amproclefttype != opcintype)
+ 			continue;
+ 
+ 		/* Check procedure numbers and function signatures */
+ 		switch (procform->amprocnum)
+ 		{
+ 			case BLOOM_HASH_PROC:
+ 				ok = check_amproc_signature(procform->amproc, INT4OID, false,
+ 											1, 1, opckeytype);
+ 				break;
+ 			default:
+ 				ereport(INFO,
+ 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 						 errmsg("bloom opfamily %s contains function %s with invalid support number %d",
+ 								opfamilyname,
+ 								format_procedure(procform->amproc),
+ 								procform->amprocnum)));
+ 				result = false;
+ 				continue;		/* don't want additional message */
+ 		}
+ 
+ 		if (!ok)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("gist opfamily %s contains function %s with wrong signature for support number %d",
+ 							opfamilyname,
+ 							format_procedure(procform->amproc),
+ 							procform->amprocnum)));
+ 			result = false;
+ 		}
+ 	}
+ 
+ 	/* Check individual operators */
+ 	for (i = 0; i < oprlist->n_members; i++)
+ 	{
+ 		HeapTuple	oprtup = &oprlist->members[i]->tuple;
+ 		Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
+ 
+ 		/* Check it's allowed strategy for bloom */
+ 		if (oprform->amopstrategy < 1 || 
+ 			oprform->amopstrategy > BLOOM_NSTRATEGIES)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains operator %s with invalid strategy number %d",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr),
+ 							oprform->amopstrategy)));
+ 			result = false;
+ 		}
+ 
+ 		/* bloom doesn't support ORDER BY operators */
+ 		if (oprform->amoppurpose != AMOP_SEARCH ||
+ 			OidIsValid(oprform->amopsortfamily))
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains invalid ORDER BY specification for operator %s",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr))));
+ 			result = false;
+ 		}
+ 
+ 		/* Check operator signature --- same for all bloom strategies */
+ 		if (!check_amop_signature(oprform->amopopr, BOOLOID,
+ 								  oprform->amoplefttype,
+ 								  oprform->amoprighttype))
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains operator %s with wrong signature",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr))));
+ 			result = false;
+ 		}
+ 	}
+ 
+ 	/* Now check for inconsistent groups of operators/functions */
+ 	grouplist = identify_opfamily_groups(oprlist, proclist);
+ 	opclassgroup = NULL;
+ 	foreach(lc, grouplist)
+ 	{
+ 		OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
+ 
+ 		/* Remember the group exactly matching the test opclass */
+ 		if (thisgroup->lefttype == opcintype &&
+ 			thisgroup->righttype == opcintype)
+ 			opclassgroup = thisgroup;
+ 
+ 		/*
+ 		 * There is not a lot we can do to check the operator sets, since each
+ 		 * bloom opclass is more or less a law unto itself, and some contain
+ 		 * only operators that are binary-compatible with the opclass datatype
+ 		 * (meaning that empty operator sets can be OK).  That case also means
+ 		 * that we shouldn't insist on nonempty function sets except for the
+ 		 * opclass's own group.
+ 		 */
+ 	}
+ 
+ 	/* Check that the originally-named opclass is complete */
+ 	for (i = 1; i <= BLOOM_NPROC; i++)
+ 	{
+ 		if (opclassgroup &&
+ 			(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
+ 			continue;			/* got it */
+ 		ereport(INFO,
+ 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 				 errmsg("bloom opclass %s is missing support function %d",
+ 						opclassname, i)));
+ 		result = false;
+ 	}
+ 
+ 	ReleaseCatCacheList(proclist);
+ 	ReleaseCatCacheList(oprlist);
+ 	ReleaseSysCache(familytup);
+ 	ReleaseSysCache(classtup);
+ 
+ 	return result;
+ }
diff --git a/contrib/bloom/data/data b/contrib/bloom/data/data
new file mode 100644
index ...eacf3e7
*** a/contrib/bloom/data/data
--- b/contrib/bloom/data/data
***************
*** 0 ****
--- 1,10000 ----
+ 739	3
+ 475	9
+ 45	6
+ 433	1
+ 948	8
+ 926	8
+ 397	7
+ 980	4
+ 212	5
+ 522	9
+ 74	8
+ 77	4
+ 378	9
+ 575	3
+ 625	2
+ 407	4
+ 509	9
+ 252	6
+ 487	7
+ 656	4
+ 485	6
+ 275	9
+ 285	3
+ 277	5
+ 804	8
+ 424	9
+ 553	5
+ 245	9
+ 384	8
+ 202	0
+ 43	6
+ 374	6
+ 490	6
+ 105	10
+ 311	8
+ 411	8
+ 343	7
+ 678	6
+ 942	10
+ 126	7
+ 755	6
+ 625	3
+ 52	9
+ 239	4
+ 690	4
+ 445	7
+ 49	8
+ 285	5
+ 445	4
+ 516	8
+ 151	9
+ 553	5
+ 613	2
+ 123	6
+ 187	2
+ 301	9
+ 800	9
+ 250	9
+ 796	5
+ 288	5
+ 930	7
+ 219	10
+ 553	5
+ 518	10
+ 893	0
+ 754	0
+ 960	3
+ 538	6
+ 537	7
+ 127	7
+ 910	4
+ 666	7
+ 354	9
+ 562	1
+ 405	8
+ 635	3
+ 583	9
+ 313	1
+ 358	8
+ 133	3
+ 864	9
+ 296	8
+ 193	8
+ 396	7
+ 495	5
+ 454	4
+ 952	1
+ 115	3
+ 36	7
+ 455	4
+ 527	1
+ 775	1
+ 945	1
+ 246	3
+ 920	4
+ 554	8
+ 267	8
+ 608	5
+ 684	0
+ 190	2
+ 527	6
+ 584	5
+ 764	7
+ 785	8
+ 376	2
+ 240	9
+ 330	0
+ 13	3
+ 103	3
+ 578	0
+ 639	1
+ 807	9
+ 982	4
+ 365	7
+ 418	6
+ 844	9
+ 199	4
+ 425	10
+ 127	2
+ 762	5
+ 450	0
+ 406	8
+ 17	4
+ 55	1
+ 678	6
+ 143	3
+ 764	9
+ 222	7
+ 364	6
+ 411	8
+ 142	3
+ 728	3
+ 684	2
+ 303	8
+ 363	1
+ 314	8
+ 67	7
+ 592	1
+ 139	6
+ 204	8
+ 279	3
+ 133	0
+ 296	4
+ 788	7
+ 942	2
+ 441	1
+ 454	2
+ 424	1
+ 322	7
+ 949	7
+ 793	3
+ 497	9
+ 984	1
+ 944	1
+ 736	1
+ 940	0
+ 494	1
+ 57	8
+ 429	8
+ 449	4
+ 44	9
+ 454	5
+ 60	9
+ 636	4
+ 606	6
+ 67	4
+ 848	6
+ 259	8
+ 654	2
+ 955	4
+ 351	9
+ 405	8
+ 968	5
+ 634	4
+ 308	1
+ 767	4
+ 974	2
+ 850	0
+ 99	5
+ 416	7
+ 71	5
+ 103	9
+ 48	4
+ 750	7
+ 565	7
+ 92	9
+ 599	5
+ 760	6
+ 960	4
+ 964	3
+ 478	7
+ 620	5
+ 953	5
+ 485	1
+ 957	9
+ 756	0
+ 383	9
+ 946	4
+ 222	7
+ 133	8
+ 401	2
+ 702	0
+ 723	5
+ 568	7
+ 857	5
+ 951	3
+ 264	6
+ 786	2
+ 42	3
+ 268	10
+ 172	0
+ 26	6
+ 884	10
+ 986	1
+ 667	1
+ 893	1
+ 344	6
+ 68	1
+ 58	6
+ 750	9
+ 168	7
+ 249	4
+ 273	0
+ 649	3
+ 306	9
+ 314	5
+ 942	3
+ 33	8
+ 311	0
+ 932	10
+ 138	8
+ 47	5
+ 420	1
+ 550	5
+ 751	3
+ 392	9
+ 1	6
+ 351	3
+ 677	10
+ 588	10
+ 917	9
+ 461	9
+ 242	5
+ 685	6
+ 514	6
+ 531	7
+ 442	6
+ 135	9
+ 693	7
+ 341	4
+ 984	7
+ 362	10
+ 375	7
+ 259	1
+ 713	8
+ 35	6
+ 750	5
+ 489	10
+ 991	2
+ 544	5
+ 791	1
+ 156	2
+ 653	3
+ 96	3
+ 976	4
+ 789	10
+ 170	2
+ 946	5
+ 865	2
+ 597	6
+ 53	6
+ 209	8
+ 128	7
+ 794	1
+ 871	3
+ 623	7
+ 413	8
+ 895	1
+ 70	10
+ 411	0
+ 428	2
+ 6	6
+ 352	10
+ 143	2
+ 156	7
+ 795	2
+ 372	0
+ 11	5
+ 701	8
+ 619	6
+ 142	2
+ 233	6
+ 20	1
+ 621	1
+ 118	0
+ 136	5
+ 232	1
+ 145	6
+ 93	3
+ 800	2
+ 28	6
+ 457	4
+ 598	5
+ 900	3
+ 273	5
+ 870	4
+ 760	1
+ 970	8
+ 231	6
+ 871	3
+ 622	0
+ 895	9
+ 148	0
+ 439	2
+ 328	2
+ 489	4
+ 834	9
+ 756	4
+ 415	7
+ 730	7
+ 175	6
+ 102	9
+ 703	1
+ 715	9
+ 662	6
+ 283	3
+ 592	2
+ 139	7
+ 218	6
+ 981	5
+ 816	5
+ 902	7
+ 417	7
+ 82	8
+ 314	8
+ 519	5
+ 413	6
+ 424	1
+ 693	1
+ 50	4
+ 725	3
+ 639	3
+ 511	8
+ 58	7
+ 356	0
+ 275	2
+ 510	2
+ 822	9
+ 835	9
+ 759	1
+ 717	3
+ 639	1
+ 899	1
+ 246	6
+ 202	3
+ 946	9
+ 629	6
+ 245	1
+ 363	3
+ 870	7
+ 342	1
+ 891	9
+ 322	7
+ 778	2
+ 617	5
+ 307	3
+ 814	9
+ 463	7
+ 8	7
+ 304	2
+ 5	2
+ 138	6
+ 835	4
+ 774	2
+ 685	6
+ 916	0
+ 789	8
+ 878	1
+ 519	7
+ 269	1
+ 193	6
+ 470	0
+ 522	9
+ 720	5
+ 643	0
+ 741	6
+ 275	9
+ 282	1
+ 261	1
+ 306	9
+ 701	2
+ 973	5
+ 28	9
+ 602	5
+ 507	9
+ 683	7
+ 448	2
+ 708	10
+ 88	4
+ 501	7
+ 453	2
+ 379	7
+ 120	7
+ 836	4
+ 717	1
+ 327	4
+ 365	3
+ 908	4
+ 151	5
+ 940	7
+ 381	6
+ 359	8
+ 777	1
+ 799	9
+ 495	3
+ 595	9
+ 542	10
+ 675	7
+ 634	5
+ 44	4
+ 654	4
+ 769	0
+ 671	7
+ 411	8
+ 187	4
+ 481	6
+ 974	8
+ 398	8
+ 907	2
+ 615	4
+ 497	2
+ 349	0
+ 183	0
+ 701	8
+ 535	7
+ 169	2
+ 116	9
+ 208	8
+ 615	6
+ 610	8
+ 970	1
+ 371	9
+ 931	8
+ 695	8
+ 966	3
+ 239	5
+ 520	6
+ 502	7
+ 612	2
+ 520	1
+ 948	7
+ 337	1
+ 627	5
+ 852	2
+ 165	5
+ 45	1
+ 554	4
+ 79	5
+ 185	8
+ 323	2
+ 84	6
+ 613	6
+ 151	1
+ 306	8
+ 318	8
+ 911	3
+ 516	2
+ 331	1
+ 793	2
+ 385	10
+ 646	4
+ 92	2
+ 846	2
+ 686	0
+ 945	0
+ 181	0
+ 572	8
+ 633	7
+ 909	9
+ 486	2
+ 766	4
+ 493	3
+ 645	8
+ 424	4
+ 8	8
+ 395	7
+ 240	5
+ 855	1
+ 659	5
+ 117	6
+ 551	3
+ 634	1
+ 93	3
+ 846	0
+ 206	3
+ 228	10
+ 730	7
+ 253	4
+ 546	7
+ 813	6
+ 487	2
+ 209	7
+ 696	1
+ 814	4
+ 605	9
+ 959	2
+ 230	6
+ 278	3
+ 860	1
+ 324	1
+ 457	6
+ 37	2
+ 273	3
+ 561	8
+ 968	4
+ 373	5
+ 582	6
+ 183	3
+ 646	10
+ 633	3
+ 928	6
+ 407	2
+ 185	7
+ 480	0
+ 810	8
+ 111	3
+ 355	1
+ 453	6
+ 439	0
+ 447	4
+ 388	8
+ 862	10
+ 402	0
+ 247	0
+ 42	9
+ 299	10
+ 472	7
+ 127	7
+ 392	6
+ 702	2
+ 410	8
+ 468	8
+ 960	9
+ 394	4
+ 935	8
+ 806	3
+ 661	7
+ 292	1
+ 714	5
+ 111	8
+ 419	4
+ 725	9
+ 117	9
+ 548	5
+ 460	2
+ 711	9
+ 62	2
+ 636	0
+ 99	0
+ 421	0
+ 870	2
+ 357	5
+ 896	6
+ 594	6
+ 189	7
+ 365	6
+ 116	1
+ 499	2
+ 943	0
+ 742	4
+ 297	5
+ 273	4
+ 631	9
+ 382	7
+ 938	8
+ 765	8
+ 30	1
+ 338	9
+ 771	9
+ 535	10
+ 637	9
+ 568	8
+ 990	1
+ 986	9
+ 114	7
+ 335	4
+ 181	6
+ 770	8
+ 517	2
+ 543	5
+ 954	3
+ 262	10
+ 430	6
+ 910	2
+ 531	4
+ 160	2
+ 345	7
+ 921	3
+ 795	9
+ 267	9
+ 635	6
+ 319	8
+ 211	1
+ 628	7
+ 240	2
+ 182	2
+ 479	4
+ 179	9
+ 43	1
+ 110	6
+ 535	3
+ 743	9
+ 999	7
+ 214	8
+ 571	5
+ 702	2
+ 85	0
+ 22	3
+ 111	6
+ 23	4
+ 821	2
+ 546	3
+ 648	7
+ 210	7
+ 814	3
+ 267	3
+ 590	0
+ 228	6
+ 674	4
+ 382	2
+ 924	1
+ 450	0
+ 106	5
+ 304	2
+ 121	3
+ 568	9
+ 532	1
+ 243	2
+ 839	5
+ 872	7
+ 772	1
+ 2	4
+ 148	2
+ 950	8
+ 673	3
+ 66	6
+ 416	5
+ 606	5
+ 987	9
+ 739	1
+ 237	3
+ 51	8
+ 422	3
+ 949	3
+ 746	8
+ 914	5
+ 959	9
+ 880	1
+ 146	8
+ 929	8
+ 163	10
+ 416	6
+ 511	0
+ 102	5
+ 932	8
+ 606	2
+ 149	7
+ 939	6
+ 951	9
+ 831	7
+ 710	7
+ 215	7
+ 662	1
+ 777	8
+ 926	7
+ 627	1
+ 701	0
+ 668	2
+ 66	8
+ 709	10
+ 611	3
+ 168	8
+ 973	1
+ 330	9
+ 996	2
+ 620	7
+ 907	8
+ 375	6
+ 931	2
+ 377	9
+ 857	0
+ 945	6
+ 49	6
+ 769	1
+ 384	5
+ 113	10
+ 794	3
+ 756	8
+ 389	1
+ 690	4
+ 248	3
+ 90	2
+ 146	5
+ 724	1
+ 616	1
+ 933	5
+ 106	9
+ 31	2
+ 492	8
+ 270	9
+ 279	4
+ 872	1
+ 664	6
+ 840	1
+ 713	5
+ 438	10
+ 841	5
+ 116	10
+ 994	8
+ 63	6
+ 942	10
+ 83	0
+ 874	1
+ 203	4
+ 914	5
+ 242	2
+ 856	1
+ 265	5
+ 742	1
+ 573	5
+ 635	0
+ 416	5
+ 540	5
+ 462	5
+ 373	5
+ 143	3
+ 520	2
+ 363	4
+ 340	6
+ 760	3
+ 40	0
+ 446	9
+ 117	7
+ 416	9
+ 816	10
+ 313	5
+ 0	7
+ 927	5
+ 261	4
+ 74	6
+ 914	2
+ 949	4
+ 443	3
+ 829	8
+ 879	6
+ 37	9
+ 591	5
+ 814	7
+ 195	2
+ 566	0
+ 218	9
+ 462	2
+ 608	4
+ 759	9
+ 778	8
+ 504	7
+ 49	5
+ 127	5
+ 765	10
+ 276	6
+ 544	3
+ 562	1
+ 797	4
+ 843	10
+ 605	4
+ 2	8
+ 288	5
+ 42	9
+ 853	8
+ 765	6
+ 633	3
+ 324	7
+ 722	5
+ 175	5
+ 406	5
+ 130	9
+ 765	7
+ 85	6
+ 68	9
+ 553	7
+ 337	6
+ 497	6
+ 19	5
+ 520	9
+ 340	3
+ 504	10
+ 554	8
+ 656	3
+ 279	8
+ 763	7
+ 283	9
+ 634	0
+ 585	7
+ 609	7
+ 647	2
+ 326	10
+ 717	8
+ 608	7
+ 362	1
+ 608	7
+ 413	1
+ 676	10
+ 940	3
+ 244	2
+ 163	0
+ 903	4
+ 899	5
+ 494	5
+ 256	1
+ 136	9
+ 264	5
+ 886	10
+ 285	5
+ 717	6
+ 621	3
+ 349	0
+ 436	0
+ 2	4
+ 356	2
+ 595	5
+ 251	5
+ 965	2
+ 34	5
+ 633	3
+ 562	8
+ 192	8
+ 231	1
+ 807	5
+ 571	5
+ 163	2
+ 848	5
+ 226	3
+ 536	2
+ 661	9
+ 473	3
+ 412	7
+ 753	4
+ 874	8
+ 837	5
+ 77	4
+ 277	3
+ 225	5
+ 347	0
+ 24	9
+ 555	2
+ 109	4
+ 699	3
+ 688	2
+ 563	3
+ 128	0
+ 604	5
+ 759	4
+ 919	6
+ 143	8
+ 141	2
+ 154	4
+ 488	4
+ 926	8
+ 410	10
+ 752	10
+ 137	9
+ 369	8
+ 197	1
+ 72	8
+ 405	2
+ 795	0
+ 741	6
+ 365	7
+ 187	5
+ 415	3
+ 728	6
+ 745	2
+ 948	7
+ 51	4
+ 621	8
+ 324	8
+ 665	7
+ 595	9
+ 750	7
+ 622	2
+ 867	4
+ 164	6
+ 971	5
+ 267	2
+ 38	7
+ 485	8
+ 251	2
+ 982	2
+ 902	0
+ 556	5
+ 836	9
+ 282	5
+ 573	9
+ 364	3
+ 543	10
+ 477	4
+ 403	6
+ 18	4
+ 171	3
+ 531	2
+ 966	0
+ 974	2
+ 247	10
+ 415	1
+ 988	10
+ 672	8
+ 851	10
+ 325	4
+ 830	7
+ 746	4
+ 675	2
+ 784	1
+ 865	8
+ 450	0
+ 86	10
+ 244	1
+ 998	2
+ 269	2
+ 173	7
+ 394	2
+ 655	1
+ 986	5
+ 20	3
+ 930	8
+ 0	7
+ 224	7
+ 900	0
+ 752	8
+ 809	2
+ 800	9
+ 184	0
+ 947	2
+ 261	2
+ 427	4
+ 899	8
+ 596	6
+ 887	6
+ 60	9
+ 894	10
+ 757	9
+ 667	10
+ 569	6
+ 987	3
+ 331	8
+ 524	1
+ 691	7
+ 174	6
+ 891	4
+ 854	3
+ 870	8
+ 139	5
+ 307	0
+ 48	4
+ 933	9
+ 358	7
+ 836	0
+ 670	4
+ 591	7
+ 726	9
+ 454	3
+ 53	1
+ 959	2
+ 783	8
+ 663	6
+ 168	5
+ 389	3
+ 999	7
+ 334	0
+ 64	3
+ 989	4
+ 957	8
+ 447	6
+ 231	0
+ 285	10
+ 960	7
+ 208	0
+ 883	2
+ 240	7
+ 16	9
+ 302	2
+ 435	7
+ 490	4
+ 388	8
+ 481	5
+ 91	5
+ 874	0
+ 296	3
+ 675	5
+ 359	10
+ 484	3
+ 698	7
+ 332	6
+ 858	6
+ 247	9
+ 475	5
+ 57	9
+ 241	5
+ 344	6
+ 371	8
+ 81	5
+ 296	10
+ 509	6
+ 277	2
+ 120	6
+ 143	6
+ 955	8
+ 296	3
+ 421	2
+ 860	7
+ 28	3
+ 217	1
+ 244	5
+ 632	6
+ 87	0
+ 414	2
+ 465	7
+ 123	10
+ 303	4
+ 158	4
+ 36	3
+ 27	10
+ 142	3
+ 278	6
+ 476	1
+ 231	5
+ 472	4
+ 588	7
+ 907	2
+ 305	10
+ 223	7
+ 161	7
+ 428	3
+ 662	7
+ 684	8
+ 154	7
+ 121	2
+ 711	3
+ 503	10
+ 826	10
+ 127	1
+ 483	6
+ 506	1
+ 316	4
+ 292	6
+ 407	5
+ 339	6
+ 203	8
+ 853	9
+ 499	5
+ 684	7
+ 257	8
+ 833	10
+ 68	3
+ 958	9
+ 316	1
+ 950	8
+ 685	5
+ 870	0
+ 869	2
+ 622	3
+ 676	10
+ 844	9
+ 729	7
+ 743	2
+ 234	4
+ 881	5
+ 233	7
+ 460	3
+ 51	4
+ 193	4
+ 503	1
+ 165	2
+ 600	0
+ 189	5
+ 197	8
+ 745	9
+ 773	6
+ 752	5
+ 285	5
+ 730	5
+ 923	6
+ 10	2
+ 324	5
+ 455	4
+ 888	6
+ 741	4
+ 792	9
+ 579	4
+ 942	8
+ 862	1
+ 580	6
+ 12	4
+ 196	8
+ 854	5
+ 259	6
+ 1	2
+ 195	0
+ 336	5
+ 481	8
+ 894	4
+ 440	6
+ 760	2
+ 542	3
+ 625	5
+ 107	5
+ 622	7
+ 94	6
+ 40	3
+ 397	9
+ 771	7
+ 479	8
+ 837	7
+ 783	2
+ 192	3
+ 964	1
+ 633	4
+ 721	4
+ 637	3
+ 732	3
+ 746	8
+ 749	4
+ 527	8
+ 2	6
+ 133	4
+ 462	9
+ 54	9
+ 677	9
+ 613	5
+ 64	8
+ 725	0
+ 891	4
+ 433	6
+ 751	1
+ 876	5
+ 332	6
+ 324	1
+ 990	9
+ 925	10
+ 418	1
+ 390	9
+ 962	4
+ 820	6
+ 335	4
+ 99	4
+ 239	8
+ 427	1
+ 182	9
+ 743	9
+ 930	6
+ 418	3
+ 241	7
+ 344	2
+ 593	3
+ 223	0
+ 326	6
+ 891	3
+ 58	7
+ 928	4
+ 145	0
+ 792	4
+ 851	2
+ 514	0
+ 80	3
+ 967	0
+ 875	4
+ 272	1
+ 126	6
+ 347	7
+ 884	6
+ 730	2
+ 184	6
+ 498	2
+ 333	4
+ 635	5
+ 453	4
+ 861	3
+ 647	4
+ 338	7
+ 632	3
+ 736	5
+ 689	0
+ 624	8
+ 623	10
+ 534	5
+ 541	3
+ 717	7
+ 885	2
+ 967	2
+ 641	6
+ 696	1
+ 29	6
+ 399	7
+ 933	7
+ 403	6
+ 41	1
+ 73	7
+ 147	7
+ 546	8
+ 668	1
+ 278	2
+ 344	10
+ 934	2
+ 209	9
+ 447	8
+ 503	1
+ 944	5
+ 700	3
+ 208	6
+ 79	6
+ 198	1
+ 750	3
+ 851	9
+ 967	4
+ 668	6
+ 477	9
+ 843	8
+ 940	8
+ 51	1
+ 678	5
+ 999	2
+ 641	9
+ 713	3
+ 285	9
+ 974	4
+ 532	2
+ 485	3
+ 442	3
+ 179	4
+ 733	8
+ 44	2
+ 792	9
+ 32	7
+ 664	1
+ 880	3
+ 581	9
+ 523	2
+ 822	2
+ 563	1
+ 157	5
+ 471	7
+ 709	10
+ 971	2
+ 292	2
+ 561	0
+ 997	6
+ 236	8
+ 491	3
+ 521	2
+ 351	4
+ 498	9
+ 281	0
+ 153	1
+ 258	7
+ 209	4
+ 253	7
+ 105	10
+ 636	1
+ 113	9
+ 227	7
+ 954	2
+ 278	2
+ 14	8
+ 459	5
+ 926	8
+ 937	4
+ 742	2
+ 446	9
+ 320	7
+ 611	5
+ 120	9
+ 210	2
+ 827	8
+ 301	9
+ 775	5
+ 614	7
+ 753	9
+ 918	8
+ 663	4
+ 302	6
+ 187	2
+ 13	9
+ 457	5
+ 824	8
+ 163	4
+ 307	3
+ 300	5
+ 508	1
+ 363	8
+ 67	1
+ 338	7
+ 866	1
+ 573	8
+ 858	2
+ 161	2
+ 824	3
+ 399	8
+ 277	9
+ 295	1
+ 633	5
+ 536	9
+ 742	8
+ 456	3
+ 963	8
+ 61	0
+ 956	4
+ 710	8
+ 490	3
+ 606	3
+ 519	8
+ 508	3
+ 116	9
+ 179	4
+ 762	5
+ 494	4
+ 934	0
+ 335	7
+ 867	8
+ 926	8
+ 610	10
+ 859	6
+ 386	6
+ 389	9
+ 852	10
+ 224	4
+ 763	7
+ 713	9
+ 638	9
+ 272	4
+ 367	8
+ 796	3
+ 796	1
+ 977	7
+ 921	9
+ 493	5
+ 890	4
+ 98	3
+ 921	5
+ 152	8
+ 482	4
+ 143	2
+ 108	9
+ 124	7
+ 750	4
+ 147	1
+ 162	9
+ 418	10
+ 73	4
+ 622	10
+ 298	1
+ 526	2
+ 466	6
+ 464	4
+ 111	6
+ 159	6
+ 992	3
+ 837	1
+ 159	10
+ 847	9
+ 357	10
+ 26	5
+ 937	4
+ 478	0
+ 839	1
+ 5	1
+ 214	5
+ 325	7
+ 156	8
+ 66	3
+ 405	2
+ 859	4
+ 527	7
+ 498	7
+ 658	3
+ 595	0
+ 339	6
+ 535	3
+ 65	0
+ 286	9
+ 112	3
+ 41	3
+ 823	4
+ 6	10
+ 154	1
+ 245	6
+ 295	1
+ 957	8
+ 800	5
+ 508	5
+ 801	1
+ 473	1
+ 723	0
+ 415	8
+ 21	7
+ 691	1
+ 993	7
+ 460	8
+ 97	5
+ 795	3
+ 536	0
+ 811	8
+ 144	8
+ 654	9
+ 224	2
+ 403	0
+ 263	9
+ 165	10
+ 884	6
+ 774	9
+ 282	5
+ 39	3
+ 197	5
+ 91	3
+ 964	9
+ 546	5
+ 926	4
+ 332	1
+ 127	10
+ 15	4
+ 146	4
+ 376	4
+ 293	5
+ 396	2
+ 120	2
+ 83	4
+ 636	1
+ 677	8
+ 620	8
+ 128	6
+ 655	7
+ 84	6
+ 32	4
+ 651	2
+ 400	7
+ 510	5
+ 83	9
+ 957	4
+ 426	4
+ 554	5
+ 523	6
+ 949	2
+ 758	6
+ 992	4
+ 395	1
+ 962	0
+ 794	0
+ 630	8
+ 461	3
+ 984	9
+ 947	5
+ 408	0
+ 380	4
+ 407	8
+ 717	10
+ 352	2
+ 598	3
+ 399	4
+ 927	4
+ 734	3
+ 510	7
+ 371	3
+ 742	0
+ 129	2
+ 283	1
+ 63	2
+ 608	5
+ 261	10
+ 835	7
+ 793	6
+ 628	1
+ 793	2
+ 446	2
+ 582	4
+ 583	3
+ 695	1
+ 13	1
+ 397	8
+ 68	5
+ 957	4
+ 641	0
+ 582	2
+ 491	8
+ 235	3
+ 510	0
+ 879	1
+ 173	7
+ 365	6
+ 863	9
+ 992	4
+ 264	7
+ 540	3
+ 754	9
+ 32	8
+ 464	10
+ 174	1
+ 9	8
+ 353	5
+ 598	6
+ 827	1
+ 616	7
+ 247	8
+ 377	6
+ 407	2
+ 558	4
+ 686	8
+ 86	2
+ 99	8
+ 163	1
+ 662	6
+ 120	8
+ 731	1
+ 591	1
+ 630	2
+ 671	5
+ 298	3
+ 162	5
+ 75	5
+ 155	5
+ 779	7
+ 880	5
+ 535	10
+ 691	6
+ 806	9
+ 764	5
+ 480	9
+ 303	2
+ 13	9
+ 294	6
+ 84	10
+ 100	4
+ 252	3
+ 926	3
+ 801	1
+ 808	6
+ 794	7
+ 45	3
+ 655	7
+ 963	5
+ 589	7
+ 929	1
+ 611	2
+ 279	6
+ 127	6
+ 267	2
+ 538	4
+ 592	8
+ 629	5
+ 117	4
+ 599	9
+ 10	4
+ 614	1
+ 722	3
+ 790	7
+ 730	4
+ 413	7
+ 447	0
+ 891	7
+ 648	0
+ 299	9
+ 228	8
+ 282	8
+ 627	9
+ 338	7
+ 340	9
+ 669	3
+ 330	3
+ 404	1
+ 552	2
+ 738	3
+ 574	2
+ 941	0
+ 174	8
+ 747	8
+ 849	0
+ 738	1
+ 884	0
+ 897	5
+ 931	2
+ 256	3
+ 173	9
+ 621	5
+ 209	0
+ 556	8
+ 220	3
+ 43	8
+ 444	10
+ 815	6
+ 816	6
+ 441	7
+ 609	2
+ 742	5
+ 199	6
+ 4	1
+ 875	3
+ 400	0
+ 185	0
+ 551	4
+ 46	1
+ 155	3
+ 400	2
+ 60	8
+ 183	9
+ 463	10
+ 436	9
+ 665	0
+ 82	4
+ 538	3
+ 47	5
+ 410	9
+ 802	8
+ 970	10
+ 832	5
+ 381	9
+ 627	5
+ 145	0
+ 734	2
+ 872	9
+ 79	3
+ 916	5
+ 238	6
+ 560	3
+ 988	1
+ 602	0
+ 639	0
+ 956	4
+ 823	9
+ 429	7
+ 446	8
+ 533	1
+ 346	7
+ 101	1
+ 883	10
+ 997	10
+ 307	9
+ 477	5
+ 495	0
+ 865	5
+ 135	5
+ 517	8
+ 479	5
+ 215	3
+ 399	6
+ 957	8
+ 454	5
+ 919	8
+ 168	0
+ 880	1
+ 992	9
+ 13	3
+ 791	5
+ 844	3
+ 527	7
+ 768	7
+ 176	3
+ 435	7
+ 759	7
+ 957	2
+ 295	9
+ 4	7
+ 403	9
+ 548	6
+ 943	4
+ 622	9
+ 305	6
+ 235	1
+ 124	1
+ 381	7
+ 789	1
+ 312	10
+ 434	7
+ 619	2
+ 398	6
+ 351	7
+ 489	4
+ 442	9
+ 279	10
+ 463	2
+ 418	1
+ 158	7
+ 720	4
+ 819	8
+ 473	2
+ 496	3
+ 349	8
+ 226	8
+ 556	8
+ 976	10
+ 421	3
+ 648	9
+ 683	1
+ 803	10
+ 80	3
+ 184	5
+ 352	3
+ 221	1
+ 736	0
+ 917	2
+ 240	4
+ 470	6
+ 221	7
+ 372	8
+ 542	3
+ 731	10
+ 676	4
+ 874	4
+ 469	7
+ 321	5
+ 943	5
+ 46	3
+ 848	3
+ 367	6
+ 307	3
+ 793	5
+ 697	3
+ 135	9
+ 959	5
+ 695	5
+ 855	4
+ 464	5
+ 806	3
+ 890	3
+ 14	2
+ 822	10
+ 715	9
+ 253	6
+ 135	6
+ 147	4
+ 904	9
+ 988	6
+ 203	1
+ 519	2
+ 630	2
+ 663	5
+ 640	1
+ 16	4
+ 465	9
+ 720	5
+ 115	5
+ 437	8
+ 410	7
+ 393	5
+ 309	5
+ 987	2
+ 479	10
+ 814	7
+ 97	3
+ 844	7
+ 547	5
+ 212	2
+ 634	2
+ 634	1
+ 133	4
+ 579	2
+ 896	0
+ 79	3
+ 706	5
+ 852	0
+ 12	8
+ 228	5
+ 813	0
+ 173	9
+ 376	0
+ 637	9
+ 524	8
+ 111	2
+ 76	7
+ 258	2
+ 98	8
+ 457	10
+ 853	5
+ 301	6
+ 8	2
+ 574	0
+ 991	8
+ 511	8
+ 845	7
+ 713	2
+ 702	4
+ 144	2
+ 199	3
+ 385	3
+ 999	6
+ 483	1
+ 481	9
+ 91	3
+ 475	4
+ 893	5
+ 544	5
+ 503	5
+ 270	0
+ 338	1
+ 698	1
+ 336	4
+ 402	5
+ 626	6
+ 735	0
+ 875	7
+ 655	4
+ 830	1
+ 298	9
+ 469	8
+ 313	4
+ 256	9
+ 830	8
+ 392	1
+ 773	7
+ 215	5
+ 782	6
+ 871	2
+ 31	5
+ 784	8
+ 509	7
+ 499	2
+ 17	3
+ 299	3
+ 250	8
+ 89	6
+ 130	3
+ 421	10
+ 104	8
+ 59	9
+ 543	3
+ 348	3
+ 824	2
+ 508	9
+ 717	3
+ 620	2
+ 950	1
+ 390	10
+ 448	7
+ 282	7
+ 457	4
+ 262	6
+ 716	7
+ 546	8
+ 496	6
+ 697	0
+ 879	0
+ 363	7
+ 265	9
+ 557	10
+ 163	2
+ 209	1
+ 296	6
+ 80	7
+ 288	4
+ 442	7
+ 733	7
+ 332	4
+ 387	9
+ 269	9
+ 483	10
+ 921	4
+ 12	3
+ 64	3
+ 155	6
+ 260	3
+ 799	5
+ 431	1
+ 68	5
+ 839	4
+ 873	3
+ 101	6
+ 986	4
+ 55	4
+ 311	3
+ 255	8
+ 290	2
+ 155	3
+ 460	2
+ 579	6
+ 840	8
+ 933	6
+ 308	4
+ 735	4
+ 875	6
+ 733	7
+ 855	8
+ 353	8
+ 268	4
+ 213	6
+ 732	5
+ 372	0
+ 644	5
+ 324	1
+ 746	9
+ 718	6
+ 743	7
+ 225	1
+ 15	10
+ 428	9
+ 534	2
+ 637	4
+ 996	10
+ 230	3
+ 399	4
+ 842	1
+ 911	2
+ 153	6
+ 741	5
+ 658	5
+ 380	4
+ 72	1
+ 28	3
+ 174	0
+ 258	6
+ 933	8
+ 763	6
+ 181	8
+ 561	4
+ 22	10
+ 854	9
+ 90	8
+ 78	2
+ 320	8
+ 719	10
+ 305	1
+ 354	4
+ 222	4
+ 675	4
+ 425	9
+ 997	4
+ 725	8
+ 928	9
+ 518	5
+ 317	5
+ 447	2
+ 405	5
+ 936	5
+ 780	3
+ 302	5
+ 233	6
+ 598	6
+ 985	8
+ 969	7
+ 215	4
+ 594	2
+ 752	3
+ 973	7
+ 224	5
+ 167	5
+ 32	6
+ 712	4
+ 152	6
+ 920	9
+ 903	2
+ 430	1
+ 830	0
+ 724	8
+ 848	7
+ 477	1
+ 88	1
+ 276	8
+ 389	2
+ 519	6
+ 740	7
+ 154	8
+ 301	9
+ 209	5
+ 514	1
+ 385	4
+ 351	8
+ 553	2
+ 843	3
+ 998	7
+ 971	5
+ 754	1
+ 545	0
+ 898	9
+ 279	4
+ 547	0
+ 104	7
+ 791	4
+ 568	10
+ 858	1
+ 129	2
+ 499	5
+ 58	1
+ 662	9
+ 330	7
+ 592	3
+ 134	3
+ 359	7
+ 376	3
+ 613	7
+ 675	2
+ 674	8
+ 862	5
+ 183	4
+ 465	0
+ 512	6
+ 284	0
+ 74	3
+ 63	7
+ 244	4
+ 395	8
+ 693	5
+ 181	1
+ 208	6
+ 309	8
+ 212	10
+ 981	9
+ 763	8
+ 352	9
+ 273	8
+ 988	8
+ 410	3
+ 796	5
+ 614	9
+ 220	9
+ 251	6
+ 693	9
+ 144	9
+ 995	4
+ 432	3
+ 174	6
+ 289	2
+ 531	1
+ 998	9
+ 997	3
+ 700	10
+ 57	1
+ 257	9
+ 594	9
+ 711	8
+ 730	10
+ 429	4
+ 905	6
+ 298	9
+ 926	7
+ 205	1
+ 374	5
+ 254	9
+ 545	3
+ 788	5
+ 524	5
+ 528	6
+ 598	8
+ 433	2
+ 656	1
+ 6	4
+ 105	4
+ 809	0
+ 8	1
+ 910	9
+ 836	1
+ 34	2
+ 608	3
+ 115	2
+ 541	9
+ 696	1
+ 391	2
+ 645	10
+ 8	1
+ 180	7
+ 220	2
+ 51	3
+ 621	9
+ 335	6
+ 966	2
+ 564	8
+ 359	6
+ 12	10
+ 887	1
+ 120	4
+ 31	8
+ 492	4
+ 40	1
+ 410	0
+ 213	6
+ 713	4
+ 777	8
+ 759	4
+ 623	1
+ 28	6
+ 338	6
+ 390	7
+ 191	4
+ 663	1
+ 530	8
+ 505	6
+ 599	10
+ 983	6
+ 133	4
+ 687	3
+ 984	4
+ 780	8
+ 163	5
+ 160	8
+ 632	2
+ 374	10
+ 780	8
+ 666	10
+ 167	3
+ 48	7
+ 112	6
+ 258	7
+ 549	2
+ 350	7
+ 635	0
+ 27	6
+ 437	8
+ 380	6
+ 345	5
+ 386	10
+ 727	8
+ 947	5
+ 525	6
+ 477	7
+ 942	5
+ 389	1
+ 77	6
+ 765	6
+ 889	1
+ 308	5
+ 153	3
+ 142	6
+ 143	5
+ 191	5
+ 62	6
+ 465	8
+ 338	4
+ 296	9
+ 25	8
+ 555	10
+ 298	9
+ 20	4
+ 591	8
+ 2	5
+ 901	3
+ 3	1
+ 645	1
+ 645	8
+ 667	8
+ 276	7
+ 413	7
+ 517	8
+ 153	8
+ 613	2
+ 586	2
+ 144	9
+ 112	2
+ 259	7
+ 949	3
+ 183	9
+ 570	2
+ 904	2
+ 331	5
+ 3	10
+ 385	3
+ 726	8
+ 20	2
+ 549	2
+ 56	2
+ 351	6
+ 330	5
+ 525	4
+ 658	8
+ 144	6
+ 45	3
+ 458	6
+ 513	4
+ 830	8
+ 911	8
+ 841	3
+ 112	6
+ 94	1
+ 810	6
+ 305	9
+ 806	7
+ 508	1
+ 150	0
+ 577	8
+ 817	7
+ 416	9
+ 49	9
+ 477	6
+ 236	3
+ 405	1
+ 140	2
+ 443	3
+ 812	5
+ 385	6
+ 181	7
+ 489	10
+ 345	10
+ 122	5
+ 30	7
+ 304	8
+ 421	7
+ 710	5
+ 594	2
+ 31	8
+ 493	4
+ 977	6
+ 681	4
+ 886	5
+ 958	3
+ 116	1
+ 960	6
+ 126	3
+ 602	2
+ 801	6
+ 948	1
+ 480	4
+ 825	2
+ 840	4
+ 376	9
+ 249	9
+ 307	2
+ 504	10
+ 646	4
+ 482	6
+ 661	6
+ 744	6
+ 203	9
+ 927	8
+ 118	7
+ 438	1
+ 833	9
+ 436	7
+ 108	3
+ 77	5
+ 147	3
+ 354	5
+ 552	9
+ 443	2
+ 248	9
+ 802	9
+ 523	5
+ 530	7
+ 416	5
+ 532	5
+ 186	10
+ 600	0
+ 887	0
+ 677	10
+ 312	8
+ 479	5
+ 80	8
+ 913	6
+ 691	4
+ 830	9
+ 281	6
+ 848	8
+ 178	4
+ 530	6
+ 835	1
+ 128	0
+ 31	7
+ 39	9
+ 765	7
+ 914	1
+ 470	4
+ 537	6
+ 226	4
+ 183	9
+ 806	0
+ 855	1
+ 645	7
+ 890	8
+ 81	4
+ 418	9
+ 482	5
+ 937	5
+ 274	10
+ 432	0
+ 692	3
+ 116	2
+ 738	7
+ 713	10
+ 102	9
+ 881	9
+ 909	7
+ 994	6
+ 439	9
+ 378	5
+ 304	8
+ 436	8
+ 341	4
+ 299	6
+ 349	7
+ 653	0
+ 76	8
+ 203	8
+ 421	9
+ 778	5
+ 812	7
+ 431	7
+ 395	4
+ 275	8
+ 309	7
+ 354	6
+ 449	8
+ 398	8
+ 163	7
+ 405	5
+ 428	1
+ 552	5
+ 828	8
+ 319	2
+ 672	1
+ 772	5
+ 756	2
+ 205	2
+ 628	5
+ 986	9
+ 134	3
+ 550	6
+ 130	9
+ 373	3
+ 644	8
+ 805	1
+ 837	4
+ 576	7
+ 113	9
+ 913	8
+ 992	7
+ 270	7
+ 889	5
+ 899	5
+ 956	9
+ 455	1
+ 225	0
+ 673	4
+ 952	0
+ 648	6
+ 825	5
+ 669	7
+ 811	2
+ 326	9
+ 140	2
+ 710	1
+ 925	10
+ 881	8
+ 454	8
+ 331	4
+ 665	8
+ 500	9
+ 791	2
+ 245	7
+ 219	9
+ 339	0
+ 347	0
+ 705	2
+ 253	0
+ 82	4
+ 270	8
+ 526	2
+ 771	4
+ 8	2
+ 186	3
+ 635	9
+ 126	1
+ 741	9
+ 307	10
+ 659	5
+ 878	10
+ 570	2
+ 5	3
+ 383	3
+ 306	5
+ 651	6
+ 256	2
+ 769	0
+ 583	8
+ 251	8
+ 117	9
+ 620	2
+ 21	4
+ 158	3
+ 346	8
+ 854	2
+ 814	4
+ 449	8
+ 699	8
+ 78	0
+ 296	7
+ 580	6
+ 905	3
+ 578	5
+ 127	8
+ 257	2
+ 715	9
+ 486	7
+ 237	6
+ 64	6
+ 461	9
+ 808	3
+ 342	3
+ 95	0
+ 89	2
+ 47	4
+ 901	6
+ 937	8
+ 977	5
+ 294	1
+ 344	6
+ 348	1
+ 428	8
+ 795	7
+ 478	9
+ 249	9
+ 777	1
+ 215	1
+ 314	3
+ 161	4
+ 482	2
+ 787	4
+ 835	7
+ 190	8
+ 238	5
+ 917	6
+ 36	3
+ 641	5
+ 100	4
+ 130	6
+ 295	4
+ 517	1
+ 436	7
+ 191	7
+ 42	4
+ 152	5
+ 559	9
+ 908	4
+ 663	1
+ 207	9
+ 583	1
+ 483	6
+ 390	1
+ 84	5
+ 561	2
+ 67	9
+ 593	6
+ 928	0
+ 316	1
+ 780	4
+ 470	9
+ 882	0
+ 871	8
+ 424	5
+ 888	6
+ 434	5
+ 756	9
+ 90	1
+ 42	2
+ 636	6
+ 387	7
+ 459	10
+ 288	4
+ 11	6
+ 505	8
+ 962	10
+ 722	8
+ 4	6
+ 634	4
+ 125	5
+ 59	6
+ 994	8
+ 476	1
+ 962	5
+ 257	6
+ 121	6
+ 301	6
+ 625	6
+ 966	6
+ 193	5
+ 426	2
+ 445	1
+ 1000	4
+ 739	6
+ 876	9
+ 157	9
+ 424	2
+ 751	9
+ 234	7
+ 418	5
+ 310	5
+ 135	6
+ 118	8
+ 200	1
+ 396	4
+ 555	8
+ 548	0
+ 969	5
+ 449	7
+ 183	3
+ 572	3
+ 261	10
+ 490	0
+ 896	7
+ 724	3
+ 214	0
+ 853	3
+ 645	10
+ 109	8
+ 56	5
+ 237	6
+ 326	8
+ 611	3
+ 334	1
+ 3	5
+ 385	6
+ 856	6
+ 571	3
+ 658	5
+ 69	4
+ 782	3
+ 415	6
+ 633	1
+ 607	7
+ 904	7
+ 248	1
+ 274	6
+ 927	9
+ 869	3
+ 945	9
+ 777	3
+ 447	6
+ 977	0
+ 978	6
+ 485	0
+ 16	3
+ 331	4
+ 902	10
+ 491	5
+ 707	4
+ 172	10
+ 537	4
+ 528	5
+ 331	4
+ 724	3
+ 268	5
+ 607	7
+ 134	6
+ 733	1
+ 219	2
+ 159	2
+ 485	5
+ 666	4
+ 455	2
+ 897	2
+ 552	1
+ 116	1
+ 515	6
+ 552	8
+ 42	3
+ 123	3
+ 777	7
+ 25	9
+ 314	8
+ 23	5
+ 975	2
+ 767	5
+ 673	4
+ 849	1
+ 591	7
+ 290	1
+ 815	4
+ 232	3
+ 51	8
+ 176	1
+ 61	3
+ 403	8
+ 28	4
+ 748	3
+ 185	8
+ 875	2
+ 953	6
+ 621	6
+ 76	5
+ 754	7
+ 216	0
+ 810	0
+ 451	0
+ 360	5
+ 826	5
+ 596	9
+ 834	10
+ 724	9
+ 426	5
+ 205	6
+ 244	1
+ 771	2
+ 724	4
+ 823	8
+ 863	6
+ 466	1
+ 622	3
+ 109	1
+ 318	5
+ 576	1
+ 6	2
+ 30	8
+ 170	8
+ 702	6
+ 226	9
+ 207	5
+ 989	10
+ 667	7
+ 372	5
+ 512	2
+ 67	10
+ 313	7
+ 254	4
+ 762	6
+ 892	3
+ 715	9
+ 510	7
+ 738	7
+ 498	4
+ 276	7
+ 348	5
+ 194	3
+ 462	9
+ 49	8
+ 350	6
+ 68	4
+ 539	4
+ 106	8
+ 804	9
+ 365	7
+ 207	1
+ 595	7
+ 824	3
+ 397	3
+ 773	7
+ 47	1
+ 156	2
+ 457	6
+ 101	5
+ 452	5
+ 66	5
+ 869	6
+ 902	10
+ 397	7
+ 844	8
+ 403	1
+ 841	10
+ 768	7
+ 330	2
+ 988	1
+ 837	0
+ 223	10
+ 276	7
+ 611	4
+ 185	1
+ 829	3
+ 583	7
+ 855	5
+ 672	3
+ 190	5
+ 14	6
+ 567	9
+ 590	3
+ 521	9
+ 498	5
+ 22	3
+ 544	2
+ 328	8
+ 925	9
+ 197	1
+ 1	0
+ 361	6
+ 723	2
+ 68	4
+ 469	3
+ 911	5
+ 851	5
+ 338	4
+ 812	9
+ 361	3
+ 368	4
+ 645	9
+ 629	10
+ 732	6
+ 911	9
+ 663	9
+ 955	0
+ 495	7
+ 241	6
+ 74	7
+ 820	10
+ 192	7
+ 462	5
+ 112	3
+ 388	5
+ 584	8
+ 856	2
+ 667	5
+ 201	4
+ 38	1
+ 329	7
+ 24	3
+ 726	5
+ 963	10
+ 81	0
+ 676	9
+ 21	9
+ 573	5
+ 398	7
+ 757	8
+ 157	3
+ 542	0
+ 569	2
+ 498	8
+ 608	5
+ 882	9
+ 238	9
+ 221	10
+ 424	2
+ 931	5
+ 221	6
+ 407	2
+ 476	10
+ 725	9
+ 664	5
+ 660	8
+ 822	2
+ 835	4
+ 411	3
+ 160	0
+ 870	0
+ 956	1
+ 947	2
+ 73	4
+ 362	0
+ 877	6
+ 612	3
+ 824	1
+ 265	5
+ 963	9
+ 31	6
+ 751	9
+ 825	6
+ 243	2
+ 920	4
+ 256	8
+ 445	2
+ 898	4
+ 390	10
+ 764	8
+ 975	6
+ 335	6
+ 926	2
+ 675	2
+ 708	6
+ 121	7
+ 261	9
+ 592	1
+ 458	8
+ 323	4
+ 238	6
+ 167	7
+ 791	1
+ 75	2
+ 36	8
+ 933	0
+ 481	3
+ 597	4
+ 427	3
+ 598	1
+ 910	7
+ 874	2
+ 590	5
+ 258	0
+ 300	6
+ 425	5
+ 160	6
+ 221	10
+ 657	3
+ 131	7
+ 134	1
+ 703	6
+ 332	3
+ 22	8
+ 573	6
+ 894	5
+ 339	8
+ 655	9
+ 234	9
+ 978	5
+ 494	4
+ 73	7
+ 995	3
+ 603	7
+ 588	7
+ 345	7
+ 799	0
+ 338	1
+ 349	4
+ 889	9
+ 980	8
+ 404	3
+ 551	1
+ 249	8
+ 972	2
+ 319	5
+ 629	4
+ 118	6
+ 685	7
+ 277	3
+ 456	6
+ 996	3
+ 670	3
+ 385	0
+ 694	3
+ 940	7
+ 57	3
+ 993	6
+ 404	2
+ 392	4
+ 468	7
+ 840	1
+ 103	10
+ 721	8
+ 680	10
+ 61	1
+ 620	1
+ 392	3
+ 391	8
+ 310	1
+ 51	3
+ 759	1
+ 595	8
+ 716	10
+ 993	1
+ 374	5
+ 819	2
+ 558	9
+ 172	3
+ 710	9
+ 278	8
+ 989	9
+ 829	4
+ 188	2
+ 158	5
+ 305	2
+ 748	1
+ 317	3
+ 815	0
+ 341	8
+ 141	7
+ 270	10
+ 929	8
+ 883	1
+ 108	6
+ 954	4
+ 364	9
+ 283	2
+ 324	5
+ 413	5
+ 970	7
+ 691	7
+ 781	0
+ 61	6
+ 41	4
+ 405	2
+ 118	7
+ 142	0
+ 504	0
+ 148	6
+ 618	1
+ 997	10
+ 46	3
+ 175	4
+ 752	6
+ 852	7
+ 306	5
+ 440	1
+ 552	5
+ 684	6
+ 904	1
+ 775	0
+ 764	9
+ 68	3
+ 942	2
+ 880	6
+ 319	9
+ 542	4
+ 157	7
+ 734	9
+ 306	6
+ 632	6
+ 130	1
+ 699	7
+ 574	4
+ 275	5
+ 472	1
+ 500	2
+ 968	6
+ 505	9
+ 785	4
+ 470	1
+ 261	0
+ 468	4
+ 730	2
+ 328	0
+ 788	10
+ 648	9
+ 32	3
+ 601	6
+ 730	9
+ 83	2
+ 926	6
+ 439	9
+ 151	9
+ 803	9
+ 327	3
+ 39	6
+ 285	5
+ 7	0
+ 709	3
+ 52	5
+ 294	7
+ 416	3
+ 47	0
+ 931	8
+ 892	0
+ 979	8
+ 597	4
+ 712	7
+ 361	5
+ 685	7
+ 789	7
+ 277	1
+ 231	3
+ 89	9
+ 618	1
+ 437	9
+ 840	9
+ 237	9
+ 869	2
+ 664	8
+ 181	6
+ 580	8
+ 61	3
+ 527	4
+ 808	2
+ 110	6
+ 936	4
+ 671	2
+ 670	8
+ 106	3
+ 901	5
+ 199	7
+ 395	4
+ 629	3
+ 603	3
+ 26	8
+ 936	6
+ 562	10
+ 898	1
+ 418	7
+ 301	5
+ 303	2
+ 915	10
+ 403	6
+ 733	5
+ 873	6
+ 52	1
+ 376	4
+ 508	0
+ 712	1
+ 297	7
+ 894	2
+ 344	5
+ 229	2
+ 546	6
+ 948	8
+ 176	3
+ 84	1
+ 225	5
+ 677	10
+ 996	5
+ 592	0
+ 622	10
+ 495	1
+ 972	2
+ 240	3
+ 944	1
+ 502	3
+ 591	7
+ 530	1
+ 379	5
+ 984	6
+ 730	1
+ 646	10
+ 555	3
+ 912	6
+ 873	5
+ 598	5
+ 472	1
+ 625	4
+ 299	9
+ 713	2
+ 1000	2
+ 531	6
+ 946	1
+ 728	3
+ 540	7
+ 881	3
+ 781	5
+ 223	3
+ 850	1
+ 885	7
+ 639	5
+ 218	1
+ 576	8
+ 555	9
+ 707	3
+ 120	7
+ 483	7
+ 298	4
+ 712	0
+ 755	3
+ 739	6
+ 521	5
+ 162	7
+ 854	0
+ 880	7
+ 735	5
+ 223	10
+ 630	8
+ 795	2
+ 676	5
+ 454	8
+ 208	9
+ 446	5
+ 367	2
+ 532	1
+ 410	3
+ 757	9
+ 789	9
+ 676	6
+ 931	6
+ 384	7
+ 74	6
+ 618	7
+ 407	4
+ 890	1
+ 915	3
+ 878	1
+ 281	3
+ 629	6
+ 482	2
+ 769	9
+ 431	5
+ 824	2
+ 445	5
+ 865	4
+ 55	2
+ 42	1
+ 855	7
+ 834	3
+ 73	7
+ 344	10
+ 68	2
+ 111	3
+ 545	7
+ 996	0
+ 901	8
+ 920	3
+ 291	7
+ 553	7
+ 243	4
+ 111	3
+ 666	2
+ 427	5
+ 812	3
+ 783	9
+ 985	1
+ 873	1
+ 348	10
+ 401	9
+ 724	4
+ 921	6
+ 163	8
+ 957	5
+ 584	5
+ 189	8
+ 928	3
+ 125	6
+ 452	6
+ 114	3
+ 814	9
+ 150	8
+ 23	0
+ 851	4
+ 7	3
+ 265	7
+ 650	2
+ 356	8
+ 25	3
+ 266	6
+ 824	5
+ 436	8
+ 754	6
+ 345	2
+ 114	5
+ 471	9
+ 356	6
+ 726	4
+ 644	6
+ 751	7
+ 829	0
+ 383	5
+ 201	7
+ 291	2
+ 53	6
+ 836	9
+ 11	3
+ 628	8
+ 834	10
+ 972	9
+ 433	4
+ 875	8
+ 63	6
+ 170	7
+ 178	9
+ 359	0
+ 937	7
+ 487	1
+ 481	8
+ 365	5
+ 335	2
+ 411	3
+ 472	0
+ 112	3
+ 13	1
+ 254	4
+ 526	1
+ 236	6
+ 730	4
+ 297	9
+ 327	7
+ 916	3
+ 399	4
+ 402	9
+ 181	8
+ 414	5
+ 967	8
+ 862	4
+ 865	10
+ 745	9
+ 58	10
+ 325	6
+ 128	6
+ 173	9
+ 967	5
+ 767	3
+ 127	7
+ 558	5
+ 86	10
+ 405	3
+ 726	8
+ 783	7
+ 645	6
+ 132	5
+ 619	9
+ 388	7
+ 876	7
+ 260	0
+ 273	4
+ 862	2
+ 903	6
+ 534	0
+ 312	1
+ 555	4
+ 51	10
+ 665	8
+ 780	4
+ 469	4
+ 93	6
+ 934	7
+ 477	3
+ 388	4
+ 34	6
+ 356	3
+ 81	2
+ 546	10
+ 847	1
+ 15	2
+ 171	6
+ 558	2
+ 531	2
+ 998	3
+ 672	5
+ 735	8
+ 67	7
+ 476	5
+ 991	9
+ 897	0
+ 512	3
+ 332	6
+ 471	9
+ 578	3
+ 958	6
+ 477	1
+ 163	0
+ 351	7
+ 259	3
+ 4	9
+ 816	7
+ 695	9
+ 409	2
+ 427	4
+ 35	3
+ 426	5
+ 576	8
+ 140	0
+ 636	7
+ 365	6
+ 311	8
+ 724	5
+ 877	1
+ 167	1
+ 424	2
+ 67	2
+ 911	8
+ 122	3
+ 932	5
+ 721	10
+ 872	1
+ 514	4
+ 905	7
+ 495	5
+ 372	9
+ 135	7
+ 702	9
+ 156	6
+ 933	3
+ 715	4
+ 495	8
+ 596	4
+ 543	7
+ 726	5
+ 267	4
+ 442	1
+ 595	10
+ 588	5
+ 610	1
+ 40	10
+ 943	2
+ 665	6
+ 34	8
+ 224	10
+ 145	9
+ 324	6
+ 721	9
+ 45	3
+ 638	8
+ 739	9
+ 219	2
+ 45	8
+ 138	6
+ 314	7
+ 717	4
+ 730	7
+ 529	4
+ 305	6
+ 216	5
+ 531	4
+ 468	9
+ 0	2
+ 776	0
+ 453	4
+ 817	2
+ 320	0
+ 373	4
+ 850	5
+ 998	2
+ 259	7
+ 518	10
+ 375	0
+ 384	7
+ 611	6
+ 209	1
+ 961	7
+ 998	10
+ 866	8
+ 7	3
+ 188	8
+ 511	5
+ 861	9
+ 873	7
+ 395	9
+ 875	7
+ 586	4
+ 643	10
+ 441	0
+ 642	1
+ 628	9
+ 195	6
+ 529	2
+ 551	4
+ 967	6
+ 714	2
+ 383	2
+ 663	2
+ 109	5
+ 955	5
+ 408	8
+ 158	10
+ 223	8
+ 956	7
+ 829	6
+ 717	5
+ 449	9
+ 46	10
+ 104	6
+ 373	1
+ 156	1
+ 226	5
+ 313	9
+ 781	4
+ 426	7
+ 926	8
+ 566	1
+ 830	8
+ 886	8
+ 454	7
+ 384	2
+ 172	8
+ 82	2
+ 811	2
+ 816	2
+ 257	10
+ 272	5
+ 509	6
+ 373	3
+ 6	8
+ 27	9
+ 635	6
+ 16	5
+ 382	9
+ 250	8
+ 617	6
+ 7	8
+ 468	1
+ 7	3
+ 275	8
+ 464	5
+ 794	7
+ 16	3
+ 320	4
+ 593	3
+ 189	6
+ 259	8
+ 213	3
+ 288	6
+ 177	5
+ 431	8
+ 173	4
+ 583	6
+ 527	6
+ 920	8
+ 413	4
+ 335	2
+ 120	4
+ 509	4
+ 740	1
+ 767	9
+ 722	0
+ 754	9
+ 301	0
+ 530	5
+ 581	10
+ 273	8
+ 400	9
+ 395	9
+ 446	3
+ 728	9
+ 700	1
+ 65	8
+ 414	6
+ 260	2
+ 676	0
+ 84	4
+ 52	8
+ 334	4
+ 880	9
+ 832	5
+ 826	1
+ 216	2
+ 960	6
+ 152	4
+ 927	9
+ 265	6
+ 943	3
+ 446	4
+ 904	7
+ 511	6
+ 733	6
+ 979	8
+ 433	3
+ 138	3
+ 177	10
+ 775	0
+ 74	10
+ 227	0
+ 603	4
+ 441	5
+ 259	7
+ 157	2
+ 37	6
+ 559	9
+ 309	1
+ 522	0
+ 666	5
+ 827	1
+ 814	10
+ 413	10
+ 935	2
+ 993	0
+ 180	2
+ 44	8
+ 599	5
+ 312	9
+ 191	5
+ 61	2
+ 73	6
+ 169	4
+ 691	7
+ 424	4
+ 192	3
+ 456	0
+ 217	9
+ 997	2
+ 57	10
+ 162	2
+ 210	2
+ 19	8
+ 690	3
+ 668	9
+ 801	7
+ 109	9
+ 350	3
+ 256	0
+ 969	7
+ 399	2
+ 932	9
+ 168	1
+ 724	2
+ 301	8
+ 154	5
+ 19	4
+ 668	0
+ 173	4
+ 370	8
+ 239	2
+ 570	3
+ 45	9
+ 626	3
+ 962	6
+ 982	4
+ 757	9
+ 216	9
+ 63	9
+ 89	4
+ 722	2
+ 828	7
+ 606	5
+ 779	8
+ 854	1
+ 619	1
+ 320	2
+ 441	4
+ 110	1
+ 666	1
+ 663	6
+ 432	4
+ 562	6
+ 344	6
+ 588	4
+ 989	3
+ 676	8
+ 51	3
+ 313	8
+ 61	2
+ 978	7
+ 260	3
+ 870	7
+ 663	10
+ 768	3
+ 50	4
+ 978	5
+ 850	5
+ 129	2
+ 165	7
+ 628	2
+ 27	3
+ 971	1
+ 586	3
+ 907	6
+ 450	9
+ 327	7
+ 184	2
+ 410	8
+ 175	2
+ 177	2
+ 608	2
+ 707	5
+ 694	8
+ 652	9
+ 554	3
+ 13	6
+ 584	10
+ 658	2
+ 267	6
+ 816	7
+ 450	1
+ 428	6
+ 339	8
+ 480	5
+ 16	7
+ 739	6
+ 811	4
+ 82	5
+ 283	7
+ 364	8
+ 15	4
+ 417	6
+ 360	1
+ 769	6
+ 640	6
+ 345	1
+ 728	8
+ 723	1
+ 611	2
+ 581	6
+ 861	3
+ 252	7
+ 767	3
+ 177	1
+ 69	5
+ 887	1
+ 918	3
+ 684	3
+ 380	5
+ 906	0
+ 38	3
+ 110	8
+ 24	8
+ 833	6
+ 37	4
+ 263	9
+ 733	5
+ 570	5
+ 849	7
+ 550	9
+ 288	4
+ 2	2
+ 742	7
+ 484	1
+ 139	4
+ 142	2
+ 640	3
+ 942	7
+ 85	8
+ 300	1
+ 188	6
+ 20	9
+ 76	6
+ 422	9
+ 336	10
+ 843	6
+ 409	8
+ 830	2
+ 531	3
+ 274	7
+ 704	4
+ 846	3
+ 667	8
+ 8	8
+ 564	3
+ 874	8
+ 870	9
+ 674	9
+ 483	1
+ 871	8
+ 68	7
+ 444	5
+ 559	3
+ 629	1
+ 588	9
+ 760	3
+ 318	6
+ 636	10
+ 395	6
+ 737	10
+ 951	6
+ 711	8
+ 505	4
+ 767	10
+ 480	6
+ 807	5
+ 353	3
+ 25	9
+ 525	7
+ 2	1
+ 555	8
+ 405	9
+ 369	0
+ 858	8
+ 684	6
+ 723	6
+ 207	4
+ 456	7
+ 818	2
+ 701	3
+ 862	5
+ 845	2
+ 759	9
+ 127	3
+ 523	1
+ 397	1
+ 892	8
+ 952	3
+ 841	8
+ 25	5
+ 406	7
+ 160	6
+ 181	6
+ 325	10
+ 840	0
+ 297	7
+ 534	1
+ 916	3
+ 13	0
+ 577	5
+ 172	10
+ 615	1
+ 775	6
+ 325	6
+ 377	3
+ 141	8
+ 97	3
+ 396	3
+ 918	7
+ 278	8
+ 747	6
+ 459	3
+ 717	4
+ 574	7
+ 418	2
+ 265	6
+ 125	9
+ 655	9
+ 447	10
+ 516	8
+ 329	7
+ 606	4
+ 959	0
+ 705	9
+ 723	10
+ 634	5
+ 557	1
+ 751	3
+ 468	3
+ 4	9
+ 477	3
+ 477	6
+ 149	1
+ 501	6
+ 111	0
+ 419	4
+ 674	0
+ 867	6
+ 28	6
+ 509	8
+ 554	1
+ 221	1
+ 236	10
+ 385	7
+ 298	4
+ 590	8
+ 657	1
+ 375	8
+ 200	9
+ 402	3
+ 893	8
+ 752	6
+ 847	6
+ 199	9
+ 190	7
+ 626	7
+ 852	8
+ 854	1
+ 819	2
+ 792	1
+ 627	4
+ 891	3
+ 450	3
+ 91	6
+ 142	5
+ 961	0
+ 315	7
+ 602	2
+ 331	8
+ 37	5
+ 510	7
+ 265	4
+ 509	1
+ 450	3
+ 358	2
+ 445	10
+ 624	3
+ 270	1
+ 602	4
+ 724	7
+ 854	7
+ 779	2
+ 397	4
+ 331	7
+ 182	4
+ 248	7
+ 31	5
+ 55	5
+ 632	5
+ 869	10
+ 746	3
+ 976	4
+ 649	2
+ 445	3
+ 606	2
+ 995	5
+ 853	8
+ 629	2
+ 155	10
+ 977	3
+ 329	2
+ 30	4
+ 738	1
+ 901	4
+ 589	8
+ 361	3
+ 84	3
+ 706	7
+ 582	2
+ 984	2
+ 319	10
+ 648	2
+ 753	3
+ 421	9
+ 237	4
+ 246	6
+ 623	3
+ 926	4
+ 361	8
+ 732	9
+ 597	1
+ 285	7
+ 430	10
+ 414	0
+ 141	4
+ 201	5
+ 378	8
+ 631	1
+ 126	1
+ 40	4
+ 449	3
+ 929	1
+ 562	9
+ 433	9
+ 682	2
+ 872	3
+ 258	2
+ 960	7
+ 147	4
+ 700	3
+ 773	9
+ 747	2
+ 749	4
+ 282	9
+ 429	3
+ 239	9
+ 608	2
+ 949	2
+ 23	4
+ 92	7
+ 546	10
+ 984	8
+ 121	9
+ 491	3
+ 318	2
+ 556	1
+ 91	3
+ 242	8
+ 680	5
+ 716	1
+ 846	10
+ 986	5
+ 123	9
+ 624	1
+ 317	7
+ 851	9
+ 681	8
+ 668	8
+ 778	2
+ 71	1
+ 350	6
+ 186	4
+ 929	4
+ 282	6
+ 952	10
+ 718	8
+ 952	7
+ 252	1
+ 639	9
+ 221	10
+ 593	1
+ 820	3
+ 906	5
+ 76	7
+ 647	1
+ 779	10
+ 774	10
+ 438	7
+ 393	7
+ 312	3
+ 718	0
+ 143	7
+ 734	4
+ 745	4
+ 271	10
+ 330	9
+ 37	1
+ 138	9
+ 637	2
+ 627	3
+ 361	4
+ 281	1
+ 372	7
+ 838	8
+ 440	1
+ 110	2
+ 180	3
+ 828	9
+ 647	6
+ 287	9
+ 538	6
+ 782	6
+ 766	9
+ 518	4
+ 133	1
+ 688	5
+ 551	10
+ 629	9
+ 689	5
+ 688	1
+ 616	8
+ 287	8
+ 50	1
+ 709	7
+ 687	10
+ 616	2
+ 613	4
+ 801	4
+ 316	3
+ 782	4
+ 464	5
+ 944	0
+ 439	6
+ 939	1
+ 39	6
+ 257	7
+ 425	5
+ 451	5
+ 658	2
+ 173	3
+ 157	8
+ 570	8
+ 186	4
+ 148	5
+ 690	9
+ 951	2
+ 400	9
+ 170	8
+ 468	1
+ 967	5
+ 735	2
+ 162	2
+ 768	6
+ 635	4
+ 774	8
+ 771	9
+ 596	3
+ 700	8
+ 712	8
+ 283	4
+ 778	2
+ 556	2
+ 129	7
+ 17	6
+ 834	10
+ 104	6
+ 208	3
+ 729	10
+ 879	4
+ 403	7
+ 171	2
+ 583	8
+ 516	3
+ 548	2
+ 131	8
+ 631	9
+ 66	2
+ 86	2
+ 912	1
+ 792	7
+ 86	9
+ 315	3
+ 161	0
+ 272	0
+ 408	7
+ 693	6
+ 850	3
+ 346	4
+ 559	9
+ 595	7
+ 726	2
+ 598	8
+ 412	7
+ 987	3
+ 786	8
+ 70	9
+ 676	4
+ 167	8
+ 430	4
+ 877	8
+ 113	6
+ 417	10
+ 846	8
+ 330	4
+ 657	9
+ 94	4
+ 150	7
+ 175	6
+ 376	2
+ 886	2
+ 942	10
+ 34	6
+ 342	2
+ 455	8
+ 639	3
+ 610	8
+ 902	0
+ 715	7
+ 789	0
+ 152	4
+ 969	2
+ 829	1
+ 938	0
+ 682	3
+ 166	6
+ 475	1
+ 525	5
+ 725	9
+ 709	2
+ 639	3
+ 511	2
+ 99	4
+ 275	8
+ 160	1
+ 859	3
+ 509	8
+ 558	3
+ 948	5
+ 341	6
+ 809	5
+ 198	3
+ 614	7
+ 792	3
+ 590	5
+ 519	2
+ 848	0
+ 478	9
+ 443	8
+ 761	6
+ 816	6
+ 916	3
+ 448	5
+ 663	4
+ 970	0
+ 26	8
+ 512	2
+ 62	1
+ 947	9
+ 465	5
+ 354	10
+ 766	2
+ 14	2
+ 149	5
+ 996	9
+ 61	8
+ 530	10
+ 138	10
+ 451	8
+ 374	4
+ 806	4
+ 199	3
+ 623	3
+ 443	6
+ 115	9
+ 107	5
+ 893	9
+ 671	9
+ 117	8
+ 365	1
+ 730	4
+ 926	3
+ 403	1
+ 237	9
+ 865	6
+ 275	7
+ 10	5
+ 988	6
+ 736	4
+ 204	9
+ 340	3
+ 321	2
+ 185	10
+ 140	3
+ 812	5
+ 416	5
+ 931	3
+ 802	3
+ 405	0
+ 189	3
+ 650	5
+ 941	7
+ 939	9
+ 295	7
+ 361	5
+ 526	7
+ 810	8
+ 934	10
+ 839	1
+ 297	7
+ 579	7
+ 194	5
+ 54	10
+ 845	5
+ 36	0
+ 730	7
+ 498	7
+ 346	4
+ 602	6
+ 112	10
+ 140	6
+ 665	9
+ 486	6
+ 945	3
+ 673	2
+ 977	3
+ 954	2
+ 763	0
+ 167	6
+ 468	2
+ 641	2
+ 888	1
+ 870	2
+ 576	5
+ 876	7
+ 434	0
+ 326	1
+ 965	8
+ 698	9
+ 136	4
+ 151	1
+ 624	1
+ 284	4
+ 114	5
+ 994	6
+ 654	6
+ 780	5
+ 773	7
+ 777	3
+ 122	7
+ 36	6
+ 669	4
+ 655	6
+ 174	4
+ 544	3
+ 724	7
+ 423	3
+ 801	7
+ 734	9
+ 158	7
+ 497	8
+ 362	3
+ 354	1
+ 928	1
+ 484	0
+ 784	5
+ 605	5
+ 882	3
+ 87	1
+ 613	6
+ 365	3
+ 326	8
+ 685	1
+ 495	4
+ 42	7
+ 148	5
+ 465	5
+ 816	8
+ 646	7
+ 950	1
+ 793	7
+ 649	4
+ 187	5
+ 658	3
+ 587	3
+ 904	10
+ 608	2
+ 740	3
+ 356	2
+ 712	4
+ 888	9
+ 937	4
+ 370	8
+ 172	0
+ 497	1
+ 146	3
+ 855	8
+ 687	0
+ 327	3
+ 315	9
+ 616	2
+ 866	2
+ 449	6
+ 516	8
+ 842	2
+ 203	7
+ 89	1
+ 83	5
+ 893	3
+ 475	4
+ 376	6
+ 679	2
+ 416	4
+ 272	7
+ 711	6
+ 656	3
+ 806	5
+ 550	3
+ 129	1
+ 60	10
+ 295	3
+ 701	4
+ 403	8
+ 843	3
+ 39	3
+ 686	4
+ 940	4
+ 645	4
+ 732	9
+ 99	4
+ 504	8
+ 770	3
+ 278	3
+ 565	4
+ 386	6
+ 377	7
+ 887	1
+ 65	3
+ 861	9
+ 586	9
+ 227	3
+ 315	2
+ 638	10
+ 523	4
+ 878	6
+ 812	4
+ 378	6
+ 693	7
+ 901	3
+ 62	3
+ 881	4
+ 968	8
+ 517	0
+ 58	4
+ 941	6
+ 278	2
+ 917	6
+ 335	6
+ 553	9
+ 923	4
+ 480	7
+ 813	9
+ 316	5
+ 514	2
+ 763	6
+ 504	6
+ 16	5
+ 413	5
+ 505	5
+ 911	4
+ 116	2
+ 614	0
+ 782	9
+ 587	3
+ 806	5
+ 767	3
+ 246	6
+ 145	6
+ 86	7
+ 780	8
+ 236	3
+ 494	3
+ 756	9
+ 785	3
+ 378	7
+ 707	5
+ 885	3
+ 527	7
+ 269	1
+ 4	1
+ 625	8
+ 362	9
+ 351	5
+ 433	4
+ 166	2
+ 287	4
+ 497	8
+ 655	3
+ 688	4
+ 514	1
+ 137	2
+ 561	0
+ 541	1
+ 690	8
+ 202	7
+ 885	8
+ 464	2
+ 698	8
+ 753	1
+ 252	9
+ 345	5
+ 322	8
+ 321	10
+ 95	0
+ 418	6
+ 76	6
+ 829	6
+ 576	4
+ 725	3
+ 180	9
+ 959	1
+ 755	4
+ 311	5
+ 238	1
+ 585	5
+ 983	9
+ 30	3
+ 772	4
+ 283	9
+ 360	7
+ 476	4
+ 256	3
+ 73	8
+ 675	8
+ 98	9
+ 726	1
+ 919	5
+ 480	2
+ 934	7
+ 293	5
+ 208	3
+ 450	2
+ 582	2
+ 588	9
+ 88	9
+ 567	6
+ 384	8
+ 869	5
+ 655	5
+ 255	8
+ 398	10
+ 810	3
+ 461	3
+ 546	4
+ 8	8
+ 915	2
+ 115	4
+ 453	7
+ 586	0
+ 562	7
+ 988	1
+ 238	4
+ 952	1
+ 829	6
+ 650	1
+ 359	0
+ 64	2
+ 365	5
+ 459	9
+ 920	5
+ 749	8
+ 683	9
+ 200	1
+ 561	8
+ 176	1
+ 460	2
+ 252	7
+ 537	2
+ 805	4
+ 810	5
+ 449	2
+ 503	5
+ 338	9
+ 37	8
+ 778	10
+ 264	5
+ 793	9
+ 390	10
+ 83	10
+ 778	3
+ 74	2
+ 424	3
+ 936	10
+ 530	7
+ 326	3
+ 196	8
+ 509	7
+ 287	8
+ 567	3
+ 645	3
+ 282	9
+ 871	1
+ 856	3
+ 68	9
+ 212	8
+ 198	3
+ 84	6
+ 613	0
+ 583	1
+ 761	9
+ 484	10
+ 684	10
+ 657	10
+ 841	2
+ 296	5
+ 569	6
+ 395	4
+ 653	3
+ 702	7
+ 190	9
+ 567	4
+ 201	7
+ 11	8
+ 670	6
+ 957	4
+ 504	4
+ 389	2
+ 435	0
+ 160	3
+ 270	5
+ 761	8
+ 34	2
+ 279	7
+ 407	10
+ 408	6
+ 894	10
+ 986	1
+ 625	10
+ 908	3
+ 592	9
+ 727	1
+ 307	1
+ 285	7
+ 162	4
+ 17	4
+ 900	8
+ 270	9
+ 934	5
+ 622	3
+ 529	0
+ 939	4
+ 5	9
+ 518	6
+ 923	4
+ 925	5
+ 292	7
+ 612	6
+ 768	9
+ 341	9
+ 341	4
+ 362	2
+ 136	6
+ 175	1
+ 181	8
+ 411	7
+ 826	4
+ 134	8
+ 275	7
+ 461	2
+ 79	4
+ 714	4
+ 38	3
+ 970	8
+ 222	3
+ 737	6
+ 668	1
+ 803	8
+ 732	10
+ 873	9
+ 774	3
+ 623	6
+ 635	8
+ 431	9
+ 409	9
+ 108	5
+ 278	8
+ 858	3
+ 147	8
+ 123	4
+ 139	9
+ 931	8
+ 959	7
+ 611	7
+ 712	5
+ 604	5
+ 769	2
+ 86	4
+ 985	5
+ 313	4
+ 408	4
+ 881	7
+ 243	7
+ 2	4
+ 568	1
+ 760	7
+ 985	7
+ 514	9
+ 426	1
+ 636	1
+ 608	2
+ 624	4
+ 467	7
+ 780	5
+ 227	1
+ 846	6
+ 514	7
+ 321	8
+ 467	3
+ 148	0
+ 448	9
+ 742	4
+ 598	3
+ 378	0
+ 380	0
+ 162	10
+ 253	8
+ 365	7
+ 495	1
+ 173	7
+ 238	0
+ 356	8
+ 746	7
+ 510	2
+ 0	7
+ 248	4
+ 565	10
+ 882	2
+ 246	3
+ 187	6
+ 272	3
+ 614	5
+ 134	10
+ 246	6
+ 125	4
+ 350	4
+ 437	7
+ 115	2
+ 384	6
+ 396	4
+ 282	6
+ 833	8
+ 634	7
+ 10	9
+ 973	2
+ 507	2
+ 545	1
+ 771	7
+ 100	0
+ 307	2
+ 435	7
+ 588	9
+ 364	7
+ 55	7
+ 327	5
+ 132	6
+ 95	10
+ 455	7
+ 679	5
+ 609	7
+ 661	1
+ 897	2
+ 237	7
+ 885	3
+ 685	2
+ 563	1
+ 849	2
+ 991	2
+ 853	0
+ 961	2
+ 497	1
+ 788	6
+ 57	2
+ 320	7
+ 708	9
+ 387	4
+ 46	3
+ 575	3
+ 953	5
+ 620	6
+ 652	2
+ 758	5
+ 333	7
+ 714	2
+ 795	7
+ 365	3
+ 767	2
+ 883	8
+ 396	2
+ 559	1
+ 133	9
+ 472	2
+ 232	0
+ 461	2
+ 507	1
+ 823	2
+ 264	6
+ 659	6
+ 329	4
+ 783	1
+ 47	1
+ 416	8
+ 300	3
+ 638	7
+ 502	2
+ 800	6
+ 145	3
+ 814	4
+ 319	3
+ 561	8
+ 356	4
+ 984	6
+ 964	6
+ 218	3
+ 16	0
+ 418	1
+ 148	8
+ 878	4
+ 133	5
+ 144	6
+ 714	9
+ 270	9
+ 217	1
+ 235	5
+ 358	8
+ 362	7
+ 180	3
+ 335	1
+ 989	6
+ 437	0
+ 553	9
+ 69	7
+ 689	9
+ 149	8
+ 463	3
+ 457	2
+ 238	7
+ 36	5
+ 810	3
+ 990	2
+ 67	4
+ 883	2
+ 698	2
+ 390	7
+ 771	8
+ 693	3
+ 683	8
+ 26	4
+ 709	2
+ 194	2
+ 469	7
+ 349	7
+ 377	4
+ 161	2
+ 656	2
+ 355	7
+ 503	2
+ 969	2
+ 456	4
+ 889	2
+ 187	6
+ 553	9
+ 344	6
+ 241	1
+ 754	4
+ 225	2
+ 85	6
+ 929	5
+ 960	1
+ 650	6
+ 241	0
+ 339	7
+ 244	3
+ 946	7
+ 668	8
+ 928	9
+ 418	5
+ 725	8
+ 59	10
+ 815	8
+ 400	0
+ 35	5
+ 614	10
+ 948	6
+ 53	6
+ 190	3
+ 604	5
+ 39	8
+ 838	10
+ 547	5
+ 820	5
+ 361	2
+ 955	1
+ 0	0
+ 52	8
+ 826	5
+ 855	9
+ 938	5
+ 825	9
+ 43	9
+ 484	2
+ 173	1
+ 762	2
+ 935	6
+ 196	5
+ 107	0
+ 957	5
+ 254	9
+ 554	3
+ 926	6
+ 69	8
+ 58	9
+ 614	10
+ 393	4
+ 882	4
+ 317	4
+ 669	5
+ 454	4
+ 701	4
+ 32	9
+ 871	1
+ 913	8
+ 607	2
+ 740	2
+ 421	7
+ 767	5
+ 418	8
+ 414	0
+ 821	8
+ 470	7
+ 244	8
+ 69	9
+ 277	5
+ 345	10
+ 912	4
+ 875	8
+ 516	8
+ 611	1
+ 956	4
+ 285	4
+ 16	1
+ 867	4
+ 878	3
+ 466	7
+ 88	9
+ 401	3
+ 723	5
+ 245	0
+ 992	6
+ 978	9
+ 967	9
+ 687	5
+ 642	3
+ 607	6
+ 648	9
+ 974	7
+ 944	8
+ 98	8
+ 122	6
+ 520	2
+ 500	9
+ 541	2
+ 391	8
+ 223	4
+ 376	2
+ 288	3
+ 55	10
+ 827	7
+ 273	4
+ 294	9
+ 325	3
+ 585	3
+ 108	7
+ 92	2
+ 248	6
+ 440	7
+ 533	10
+ 971	9
+ 767	2
+ 308	1
+ 395	6
+ 487	4
+ 571	3
+ 146	8
+ 747	4
+ 765	1
+ 707	4
+ 342	8
+ 34	4
+ 46	3
+ 46	5
+ 30	6
+ 466	0
+ 504	2
+ 194	8
+ 378	6
+ 408	9
+ 38	10
+ 178	2
+ 823	9
+ 624	6
+ 997	3
+ 939	3
+ 147	10
+ 772	2
+ 255	8
+ 677	3
+ 397	1
+ 286	9
+ 378	5
+ 712	8
+ 69	1
+ 620	1
+ 98	8
+ 291	9
+ 722	9
+ 509	7
+ 245	4
+ 58	4
+ 421	8
+ 584	7
+ 648	3
+ 962	0
+ 405	2
+ 945	8
+ 727	7
+ 538	8
+ 776	2
+ 903	9
+ 956	2
+ 796	7
+ 108	3
+ 397	4
+ 753	5
+ 745	2
+ 285	3
+ 850	9
+ 591	8
+ 977	10
+ 59	9
+ 780	8
+ 578	3
+ 583	4
+ 476	5
+ 228	4
+ 679	0
+ 110	8
+ 329	5
+ 141	1
+ 963	9
+ 255	2
+ 215	1
+ 181	8
+ 917	2
+ 803	10
+ 80	6
+ 763	7
+ 900	3
+ 12	4
+ 831	2
+ 809	5
+ 264	9
+ 297	6
+ 427	4
+ 674	4
+ 324	9
+ 638	5
+ 34	8
+ 346	10
+ 978	1
+ 928	1
+ 730	7
+ 716	6
+ 36	7
+ 7	9
+ 969	8
+ 378	2
+ 735	7
+ 826	2
+ 113	5
+ 552	4
+ 429	2
+ 976	5
+ 10	3
+ 415	10
+ 470	3
+ 46	2
+ 33	8
+ 831	1
+ 490	8
+ 937	5
+ 654	3
+ 691	4
+ 990	5
+ 550	1
+ 17	1
+ 539	4
+ 292	5
+ 909	3
+ 837	3
+ 289	3
+ 666	3
+ 507	7
+ 96	3
+ 769	6
+ 176	7
+ 45	8
+ 21	7
+ 218	0
+ 253	8
+ 113	3
+ 870	7
+ 715	2
+ 167	6
+ 463	0
+ 947	8
+ 312	6
+ 87	8
+ 312	2
+ 157	1
+ 770	3
+ 787	8
+ 163	8
+ 551	4
+ 818	8
+ 150	9
+ 74	0
+ 583	8
+ 182	8
+ 414	6
+ 755	4
+ 397	1
+ 974	5
+ 886	3
+ 668	0
+ 368	4
+ 377	2
+ 253	5
+ 964	8
+ 921	8
+ 609	1
+ 713	7
+ 90	3
+ 472	3
+ 47	9
+ 917	8
+ 247	3
+ 869	2
+ 798	8
+ 508	5
+ 798	9
+ 905	2
+ 32	2
+ 714	10
+ 962	6
+ 777	6
+ 705	5
+ 253	8
+ 787	7
+ 67	8
+ 612	10
+ 636	9
+ 298	5
+ 80	1
+ 259	6
+ 563	1
+ 464	5
+ 232	5
+ 625	9
+ 491	6
+ 581	3
+ 157	3
+ 759	4
+ 82	5
+ 136	1
+ 380	7
+ 132	0
+ 607	4
+ 520	7
+ 526	8
+ 275	1
+ 837	7
+ 556	1
+ 235	2
+ 15	7
+ 768	6
+ 994	9
+ 882	8
+ 336	10
+ 299	5
+ 112	7
+ 220	2
+ 695	8
+ 675	2
+ 513	2
+ 995	8
+ 290	8
+ 527	8
+ 900	8
+ 27	9
+ 488	8
+ 510	5
+ 720	4
+ 235	1
+ 355	5
+ 528	5
+ 213	7
+ 711	9
+ 574	4
+ 122	1
+ 587	1
+ 876	9
+ 948	4
+ 723	8
+ 165	7
+ 764	7
+ 545	3
+ 134	3
+ 666	4
+ 321	0
+ 903	8
+ 489	1
+ 597	2
+ 23	2
+ 586	1
+ 259	2
+ 263	1
+ 50	2
+ 537	8
+ 60	7
+ 522	8
+ 354	1
+ 98	5
+ 331	8
+ 857	7
+ 786	8
+ 501	3
+ 876	1
+ 475	9
+ 269	1
+ 45	5
+ 234	3
+ 662	3
+ 518	2
+ 56	6
+ 900	6
+ 402	3
+ 644	5
+ 743	10
+ 264	6
+ 627	1
+ 360	1
+ 325	2
+ 226	8
+ 134	5
+ 861	2
+ 22	1
+ 486	7
+ 379	0
+ 882	4
+ 582	8
+ 12	10
+ 37	7
+ 484	8
+ 631	7
+ 379	3
+ 799	7
+ 387	1
+ 974	6
+ 925	1
+ 108	8
+ 287	1
+ 881	8
+ 813	3
+ 778	7
+ 695	4
+ 478	7
+ 344	5
+ 364	8
+ 294	10
+ 577	7
+ 254	4
+ 412	6
+ 500	4
+ 254	4
+ 495	4
+ 211	8
+ 491	1
+ 555	3
+ 352	3
+ 1000	0
+ 693	5
+ 755	0
+ 992	1
+ 865	3
+ 114	4
+ 959	4
+ 818	4
+ 9	3
+ 757	3
+ 743	3
+ 625	10
+ 34	1
+ 46	6
+ 421	4
+ 923	4
+ 445	6
+ 898	2
+ 653	9
+ 319	5
+ 175	4
+ 960	1
+ 802	8
+ 505	8
+ 96	3
+ 75	8
+ 515	7
+ 793	5
+ 817	8
+ 139	2
+ 236	1
+ 658	7
+ 677	6
+ 882	3
+ 445	2
+ 848	6
+ 635	8
+ 754	4
+ 586	3
+ 249	7
+ 523	3
+ 522	0
+ 24	3
+ 587	8
+ 153	7
+ 78	4
+ 787	7
+ 71	5
+ 291	10
+ 794	7
+ 155	6
+ 356	8
+ 451	1
+ 228	0
+ 370	5
+ 719	9
+ 801	2
+ 930	8
+ 556	5
+ 667	7
+ 242	7
+ 98	0
+ 481	2
+ 493	8
+ 123	3
+ 508	3
+ 929	9
+ 68	4
+ 974	3
+ 417	3
+ 772	1
+ 237	6
+ 378	2
+ 399	9
+ 683	1
+ 642	9
+ 811	7
+ 954	3
+ 910	4
+ 64	0
+ 734	6
+ 310	7
+ 437	4
+ 43	4
+ 674	5
+ 756	4
+ 596	10
+ 20	10
+ 158	4
+ 907	8
+ 485	5
+ 766	3
+ 290	7
+ 588	2
+ 167	7
+ 233	9
+ 224	5
+ 564	7
+ 922	6
+ 73	6
+ 67	8
+ 41	7
+ 820	1
+ 637	10
+ 480	5
+ 820	10
+ 94	6
+ 260	4
+ 306	8
+ 584	5
+ 500	8
+ 374	7
+ 361	9
+ 385	3
+ 545	5
+ 877	6
+ 286	9
+ 275	1
+ 979	9
+ 85	5
+ 457	9
+ 424	6
+ 492	7
+ 936	8
+ 531	5
+ 271	0
+ 337	6
+ 755	7
+ 583	1
+ 980	1
+ 599	9
+ 739	9
+ 776	0
+ 992	8
+ 926	1
+ 215	4
+ 982	6
+ 935	5
+ 322	9
+ 272	9
+ 391	5
+ 885	7
+ 189	6
+ 426	8
+ 780	4
+ 899	4
+ 264	6
+ 264	0
+ 652	3
+ 796	6
+ 332	0
+ 961	3
+ 649	9
+ 789	10
+ 767	1
+ 825	2
+ 605	7
+ 886	8
+ 349	3
+ 566	1
+ 719	5
+ 508	10
+ 103	8
+ 23	8
+ 28	8
+ 333	4
+ 830	3
+ 675	5
+ 190	5
+ 450	10
+ 525	3
+ 115	1
+ 984	0
+ 924	3
+ 313	5
+ 463	0
+ 955	10
+ 15	1
+ 743	0
+ 813	8
+ 858	1
+ 131	7
+ 440	8
+ 167	6
+ 270	6
+ 587	8
+ 892	7
+ 925	9
+ 702	8
+ 210	0
+ 339	7
+ 47	3
+ 643	1
+ 351	4
+ 101	2
+ 157	10
+ 310	3
+ 647	7
+ 93	8
+ 380	4
+ 432	10
+ 158	3
+ 668	1
+ 201	4
+ 933	4
+ 386	3
+ 83	4
+ 566	7
+ 496	9
+ 113	6
+ 81	3
+ 556	4
+ 557	2
+ 140	7
+ 16	5
+ 13	4
+ 487	2
+ 772	2
+ 253	10
+ 526	2
+ 384	9
+ 458	5
+ 345	0
+ 194	8
+ 941	3
+ 438	0
+ 577	10
+ 413	1
+ 196	6
+ 784	2
+ 74	8
+ 660	6
+ 967	4
+ 716	2
+ 405	2
+ 407	8
+ 154	9
+ 256	5
+ 888	4
+ 341	8
+ 757	8
+ 852	3
+ 771	3
+ 468	10
+ 819	3
+ 179	9
+ 49	8
+ 454	0
+ 271	2
+ 238	7
+ 413	6
+ 465	6
+ 509	7
+ 67	4
+ 171	4
+ 226	9
+ 186	1
+ 261	10
+ 343	7
+ 924	2
+ 982	1
+ 55	0
+ 942	5
+ 48	2
+ 679	3
+ 890	1
+ 930	4
+ 659	4
+ 75	7
+ 836	2
+ 133	1
+ 173	3
+ 141	4
+ 277	5
+ 164	2
+ 646	1
+ 305	7
+ 178	2
+ 210	2
+ 460	9
+ 512	4
+ 981	4
+ 705	6
+ 881	8
+ 366	7
+ 26	5
+ 780	2
+ 818	9
+ 634	1
+ 404	8
+ 296	0
+ 945	6
+ 751	1
+ 848	10
+ 349	3
+ 850	9
+ 658	8
+ 303	4
+ 471	2
+ 143	8
+ 902	2
+ 335	7
+ 368	2
+ 602	0
+ 248	0
+ 800	5
+ 55	7
+ 145	8
+ 868	10
+ 767	2
+ 301	6
+ 78	10
+ 447	4
+ 322	9
+ 566	5
+ 754	5
+ 633	1
+ 149	0
+ 242	8
+ 2	5
+ 757	8
+ 35	8
+ 547	2
+ 618	4
+ 174	4
+ 631	5
+ 1	7
+ 434	4
+ 91	8
+ 366	7
+ 221	1
+ 124	9
+ 208	3
+ 855	5
+ 25	9
+ 941	8
+ 660	10
+ 593	2
+ 157	2
+ 621	3
+ 596	3
+ 806	6
+ 962	2
+ 45	1
+ 996	4
+ 709	2
+ 530	8
+ 72	7
+ 107	9
+ 189	1
+ 784	1
+ 913	4
+ 106	5
+ 650	3
+ 717	3
+ 594	3
+ 524	4
+ 910	5
+ 640	10
+ 538	6
+ 365	2
+ 854	9
+ 80	9
+ 634	2
+ 852	8
+ 318	6
+ 953	2
+ 80	1
+ 737	7
+ 323	5
+ 2	9
+ 766	5
+ 317	7
+ 11	10
+ 630	5
+ 593	10
+ 795	4
+ 891	9
+ 372	5
+ 61	2
+ 348	4
+ 861	3
+ 610	9
+ 360	3
+ 672	7
+ 800	7
+ 599	6
+ 199	9
+ 242	2
+ 873	9
+ 759	5
+ 868	6
+ 912	8
+ 429	3
+ 284	5
+ 507	6
+ 869	4
+ 933	5
+ 309	3
+ 825	10
+ 976	6
+ 654	6
+ 190	9
+ 491	4
+ 63	4
+ 304	8
+ 829	2
+ 377	7
+ 931	8
+ 24	2
+ 295	5
+ 848	2
+ 899	8
+ 642	2
+ 74	5
+ 188	0
+ 92	8
+ 624	3
+ 695	1
+ 714	8
+ 479	0
+ 581	3
+ 191	10
+ 49	1
+ 763	1
+ 337	1
+ 604	2
+ 222	5
+ 965	9
+ 712	0
+ 332	9
+ 88	4
+ 742	7
+ 706	4
+ 828	4
+ 196	3
+ 438	8
+ 616	6
+ 735	7
+ 751	5
+ 738	1
+ 556	3
+ 272	8
+ 846	2
+ 643	6
+ 277	10
+ 457	4
+ 398	2
+ 77	1
+ 636	9
+ 524	8
+ 213	10
+ 609	8
+ 591	3
+ 493	3
+ 842	2
+ 430	4
+ 573	7
+ 177	4
+ 940	8
+ 977	2
+ 795	4
+ 581	2
+ 633	7
+ 297	3
+ 564	8
+ 101	8
+ 783	7
+ 605	4
+ 55	1
+ 716	9
+ 329	1
+ 296	9
+ 847	5
+ 321	8
+ 294	3
+ 3	1
+ 731	6
+ 281	4
+ 243	6
+ 634	8
+ 399	7
+ 583	2
+ 445	2
+ 555	5
+ 286	3
+ 398	6
+ 417	7
+ 517	3
+ 167	8
+ 51	5
+ 135	1
+ 549	9
+ 638	8
+ 231	9
+ 409	9
+ 687	8
+ 599	3
+ 989	0
+ 458	5
+ 545	7
+ 816	9
+ 359	2
+ 637	9
+ 496	8
+ 713	5
+ 265	8
+ 601	8
+ 715	2
+ 645	9
+ 119	1
+ 810	8
+ 862	4
+ 76	9
+ 454	5
+ 395	10
+ 279	2
+ 942	6
+ 442	6
+ 513	9
+ 383	2
+ 486	6
+ 73	1
+ 463	8
+ 325	1
+ 733	4
+ 162	5
+ 251	0
+ 952	3
+ 874	4
+ 862	3
+ 405	1
+ 479	3
+ 778	9
+ 925	3
+ 860	3
+ 516	3
+ 956	6
+ 433	4
+ 377	8
+ 527	1
+ 203	7
+ 654	5
+ 713	6
+ 781	6
+ 12	6
+ 856	4
+ 783	3
+ 763	6
+ 257	7
+ 852	1
+ 995	4
+ 463	10
+ 957	9
+ 369	3
+ 654	9
+ 445	9
+ 584	1
+ 310	3
+ 704	1
+ 884	7
+ 734	7
+ 132	5
+ 75	9
+ 79	3
+ 582	9
+ 449	6
+ 299	9
+ 527	3
+ 808	9
+ 590	5
+ 791	0
+ 318	4
+ 134	6
+ 671	8
+ 721	6
+ 554	5
+ 295	7
+ 973	4
+ 582	1
+ 702	2
+ 983	2
+ 741	3
+ 63	3
+ 538	9
+ 163	1
+ 333	10
+ 164	7
+ 329	3
+ 280	10
+ 136	0
+ 555	7
+ 455	8
+ 377	4
+ 220	10
+ 480	9
+ 122	5
+ 72	9
+ 744	1
+ 130	3
+ 6	3
+ 410	3
+ 248	6
+ 989	6
+ 872	3
+ 577	0
+ 270	1
+ 697	7
+ 981	1
+ 153	2
+ 32	6
+ 122	2
+ 96	2
+ 16	8
+ 329	1
+ 122	3
+ 440	5
+ 673	7
+ 107	7
+ 265	10
+ 932	8
+ 986	2
+ 972	7
+ 927	10
+ 757	1
+ 154	8
+ 713	3
+ 942	8
+ 470	10
+ 649	8
+ 104	8
+ 134	5
+ 305	8
+ 232	4
+ 469	5
+ 390	4
+ 338	4
+ 602	3
+ 58	5
+ 264	8
+ 609	4
+ 603	3
+ 694	5
+ 131	2
+ 503	8
+ 962	6
+ 552	1
+ 152	9
+ 902	4
+ 268	4
+ 881	7
+ 772	2
+ 33	4
+ 529	1
+ 903	8
+ 905	5
+ 210	5
+ 833	9
+ 53	10
+ 67	6
+ 744	0
+ 164	3
+ 125	3
+ 153	0
+ 699	4
+ 398	6
+ 78	2
+ 798	1
+ 544	3
+ 202	4
+ 119	1
+ 959	3
+ 615	8
+ 232	7
+ 756	3
+ 224	5
+ 328	4
+ 797	5
+ 703	10
+ 480	4
+ 371	9
+ 982	4
+ 49	8
+ 561	6
+ 106	8
+ 40	2
+ 869	10
+ 554	5
+ 790	8
+ 151	5
+ 85	4
+ 47	4
+ 763	8
+ 866	5
+ 794	3
+ 868	2
+ 225	8
+ 615	3
+ 629	2
+ 866	7
+ 937	9
+ 960	8
+ 904	5
+ 290	7
+ 301	4
+ 241	4
+ 816	3
+ 799	6
+ 131	7
+ 45	9
+ 12	9
+ 90	2
+ 762	7
+ 510	4
+ 880	4
+ 126	8
+ 282	1
+ 623	2
+ 601	9
+ 880	9
+ 354	1
+ 287	2
+ 408	1
+ 749	5
+ 753	8
+ 464	8
+ 707	6
+ 2	5
+ 258	5
+ 859	1
+ 888	10
+ 956	2
+ 71	6
+ 355	7
+ 492	2
+ 574	8
+ 355	9
+ 15	8
+ 948	8
+ 302	7
+ 558	8
+ 466	3
+ 320	5
+ 733	6
+ 980	6
+ 716	9
+ 577	7
+ 37	6
+ 251	4
+ 321	7
+ 627	9
+ 588	10
+ 756	6
+ 746	7
+ 367	0
+ 405	9
+ 814	9
+ 191	1
+ 338	9
+ 712	3
+ 517	4
+ 186	1
+ 100	2
+ 743	4
+ 615	1
+ 93	2
+ 958	7
+ 225	7
+ 284	10
+ 418	7
+ 19	8
+ 577	8
+ 693	8
+ 967	0
+ 692	7
+ 349	2
+ 106	5
+ 303	2
+ 758	0
+ 557	4
+ 109	7
+ 616	1
+ 332	8
+ 782	6
+ 812	2
+ 267	8
+ 22	8
+ 665	7
+ 612	6
+ 746	3
+ 309	1
+ 512	4
+ 630	8
+ 622	4
+ 860	2
+ 762	10
+ 830	4
+ 37	2
+ 219	8
+ 777	0
+ 19	0
+ 863	0
+ 888	5
+ 756	5
+ 159	5
+ 804	5
+ 597	3
+ 884	2
+ 131	5
+ 616	10
+ 685	4
+ 961	5
+ 756	10
+ 675	10
+ 818	5
+ 6	8
+ 496	9
+ 878	4
+ 397	6
+ 884	6
+ 135	7
+ 23	7
+ 3	9
+ 959	1
+ 412	6
+ 125	1
+ 953	1
+ 611	7
+ 84	3
+ 683	9
+ 739	7
+ 738	2
+ 559	6
+ 619	10
+ 249	5
+ 511	4
+ 190	5
+ 116	2
+ 442	1
+ 327	9
+ 649	5
+ 951	6
+ 538	6
+ 310	6
+ 848	10
+ 524	6
+ 684	3
+ 822	2
+ 878	4
+ 198	1
+ 943	7
+ 512	1
+ 244	6
+ 325	7
+ 702	7
+ 539	4
+ 104	5
+ 952	6
+ 52	3
+ 264	9
+ 257	8
+ 487	9
+ 50	3
+ 183	9
+ 748	4
+ 56	7
+ 91	6
+ 823	3
+ 195	1
+ 21	9
+ 801	6
+ 247	9
+ 50	2
+ 546	1
+ 462	8
+ 2	7
+ 597	5
+ 659	6
+ 797	8
+ 575	5
+ 224	6
+ 236	3
+ 198	1
+ 650	4
+ 208	7
+ 289	0
+ 231	5
+ 913	3
+ 735	5
+ 383	2
+ 268	4
+ 915	9
+ 874	6
+ 512	7
+ 417	1
+ 215	6
+ 718	5
+ 955	9
+ 511	6
+ 309	7
+ 275	6
+ 727	5
+ 133	6
+ 786	9
+ 99	2
+ 64	4
+ 554	10
+ 233	4
+ 554	7
+ 98	10
+ 832	3
+ 611	5
+ 765	6
+ 466	3
+ 170	8
+ 995	4
+ 371	7
+ 951	5
+ 363	7
+ 371	5
+ 907	4
+ 830	5
+ 414	1
+ 889	10
+ 808	10
+ 937	6
+ 301	5
+ 189	1
+ 114	7
+ 343	3
+ 429	3
+ 729	8
+ 61	7
+ 304	4
+ 416	7
+ 886	3
+ 110	7
+ 784	5
+ 779	7
+ 491	6
+ 660	4
+ 226	10
+ 976	4
+ 28	1
+ 71	4
+ 374	5
+ 709	1
+ 300	8
+ 782	6
+ 193	2
+ 280	1
+ 521	4
+ 794	3
+ 913	6
+ 978	4
+ 159	6
+ 833	4
+ 600	8
+ 801	6
+ 899	9
+ 999	3
+ 371	7
+ 376	7
+ 477	2
+ 276	7
+ 356	6
+ 749	9
+ 945	5
+ 183	9
+ 116	2
+ 262	3
+ 799	1
+ 661	4
+ 904	5
+ 28	8
+ 334	0
+ 76	7
+ 735	5
+ 376	2
+ 609	7
+ 882	10
+ 207	6
+ 843	2
+ 174	0
+ 10	3
+ 187	3
+ 565	10
+ 366	2
+ 386	3
+ 689	4
+ 73	0
+ 441	1
+ 727	2
+ 600	1
+ 388	2
+ 756	3
+ 176	10
+ 901	0
+ 115	1
+ 45	1
+ 364	2
+ 396	9
+ 218	8
+ 156	6
+ 32	8
+ 18	1
+ 867	5
+ 254	6
+ 635	9
+ 699	0
+ 65	5
+ 293	2
+ 417	2
+ 259	5
+ 268	3
+ 656	6
+ 535	1
+ 562	8
+ 814	7
+ 357	8
+ 563	4
+ 952	4
+ 834	2
+ 25	5
+ 60	7
+ 492	1
+ 178	8
+ 365	6
+ 977	6
+ 127	2
+ 928	8
+ 877	5
+ 834	4
+ 216	6
+ 157	6
+ 495	7
+ 949	4
+ 150	8
+ 653	2
+ 252	7
+ 898	7
+ 838	1
+ 527	2
+ 671	5
+ 827	8
+ 750	8
+ 581	6
+ 217	4
+ 66	4
+ 64	2
+ 7	6
+ 943	10
+ 6	1
+ 738	7
+ 267	10
+ 372	2
+ 733	2
+ 242	3
+ 413	9
+ 765	2
+ 712	5
+ 994	3
+ 142	2
+ 708	2
+ 645	8
+ 431	7
+ 331	4
+ 608	3
+ 466	3
+ 996	7
+ 336	4
+ 899	1
+ 577	1
+ 330	10
+ 54	1
+ 229	8
+ 609	2
+ 59	8
+ 435	8
+ 959	1
+ 539	4
+ 733	9
+ 763	3
+ 207	2
+ 687	2
+ 961	0
+ 570	9
+ 93	1
+ 1	4
+ 137	1
+ 517	4
+ 821	1
+ 590	9
+ 878	0
+ 646	8
+ 106	2
+ 226	8
+ 56	10
+ 180	3
+ 218	9
+ 466	2
+ 891	0
+ 39	10
+ 183	0
+ 407	3
+ 95	9
+ 686	9
+ 51	3
+ 795	9
+ 301	4
+ 765	4
+ 627	10
+ 246	7
+ 981	4
+ 946	2
+ 294	4
+ 378	2
+ 448	4
+ 170	6
+ 457	6
+ 950	6
+ 501	6
+ 467	6
+ 911	3
+ 480	2
+ 704	2
+ 619	3
+ 237	9
+ 14	2
+ 291	10
+ 416	6
+ 372	8
+ 770	8
+ 212	9
+ 451	7
+ 516	4
+ 221	0
+ 37	7
+ 568	9
+ 950	0
+ 160	7
+ 293	8
+ 985	5
+ 644	10
+ 747	9
+ 960	2
+ 519	3
+ 958	3
+ 151	2
+ 227	6
+ 838	7
+ 3	1
+ 760	0
+ 747	3
+ 988	7
+ 376	1
+ 351	7
+ 928	3
+ 198	6
+ 336	9
+ 506	3
+ 109	0
+ 627	1
+ 312	8
+ 236	5
+ 380	1
+ 283	4
+ 133	0
+ 423	9
+ 371	4
+ 577	7
+ 559	9
+ 415	5
+ 264	6
+ 58	6
+ 559	6
+ 896	7
+ 588	5
+ 734	9
+ 302	10
+ 440	7
+ 45	7
+ 66	2
+ 766	5
+ 58	1
+ 899	6
+ 883	5
+ 562	3
+ 945	8
+ 911	0
+ 427	5
+ 566	3
+ 138	2
+ 847	9
+ 55	1
+ 842	5
+ 832	9
+ 218	9
+ 66	10
+ 386	1
+ 120	3
+ 758	0
+ 743	3
+ 300	7
+ 147	2
+ 690	6
+ 681	3
+ 898	8
+ 411	7
+ 691	5
+ 895	5
+ 960	7
+ 420	2
+ 625	5
+ 162	0
+ 610	3
+ 296	4
+ 283	0
+ 688	6
+ 726	8
+ 794	4
+ 410	5
+ 673	3
+ 294	1
+ 54	10
+ 549	9
+ 518	5
+ 677	9
+ 687	3
+ 425	8
+ 314	0
+ 130	6
+ 402	4
+ 648	1
+ 997	4
+ 926	8
+ 791	3
+ 267	5
+ 645	6
+ 547	7
+ 546	1
+ 649	1
+ 605	3
+ 3	3
+ 628	4
+ 141	9
+ 462	3
+ 551	9
+ 684	2
+ 954	7
+ 574	9
+ 472	4
+ 217	7
+ 828	9
+ 299	4
+ 562	8
+ 471	2
+ 909	1
+ 536	9
+ 367	2
+ 339	5
+ 106	8
+ 779	7
+ 664	5
+ 856	6
+ 144	4
+ 499	6
+ 796	7
+ 353	6
+ 579	7
+ 999	1
+ 497	5
+ 351	4
+ 546	9
+ 317	9
+ 51	7
+ 421	2
+ 456	2
+ 813	1
+ 664	7
+ 738	8
+ 100	2
+ 422	9
+ 953	8
+ 520	5
+ 428	5
+ 672	9
+ 990	0
+ 331	5
+ 910	6
+ 448	10
+ 305	9
+ 118	8
+ 70	9
+ 881	7
+ 601	6
+ 541	7
+ 855	10
+ 597	8
+ 739	1
+ 341	2
+ 637	0
+ 93	6
+ 37	4
+ 162	9
+ 73	6
+ 908	4
+ 480	0
+ 139	6
+ 957	0
+ 284	6
+ 638	8
+ 259	5
+ 788	9
+ 302	5
+ 974	6
+ 695	6
+ 656	8
+ 237	7
+ 212	4
+ 639	3
+ 9	5
+ 663	5
+ 573	8
+ 39	5
+ 821	3
+ 88	5
+ 148	3
+ 952	9
+ 204	3
+ 464	2
+ 896	2
+ 789	6
+ 947	0
+ 244	2
+ 425	9
+ 444	4
+ 430	1
+ 924	0
+ 909	10
+ 533	7
+ 286	6
+ 189	4
+ 969	1
+ 370	2
+ 394	8
+ 350	3
+ 993	1
+ 842	9
+ 165	1
+ 99	6
+ 969	5
+ 24	4
+ 651	9
+ 401	6
+ 911	9
+ 290	2
+ 556	5
+ 631	5
+ 619	0
+ 696	0
+ 835	0
+ 303	8
+ 185	1
+ 767	3
+ 231	9
+ 940	2
+ 410	10
+ 598	1
+ 912	10
+ 621	8
+ 934	9
+ 20	5
+ 389	7
+ 14	0
+ 651	7
+ 22	5
+ 757	3
+ 313	9
+ 471	1
+ 292	7
+ 947	2
+ 902	4
+ 196	5
+ 418	1
+ 500	0
+ 931	4
+ 949	10
+ 924	3
+ 601	9
+ 348	3
+ 648	4
+ 738	4
+ 695	1
+ 347	2
+ 132	6
+ 867	1
+ 872	8
+ 436	1
+ 269	9
+ 176	8
+ 893	1
+ 203	8
+ 59	1
+ 181	7
+ 65	5
+ 912	7
+ 898	7
+ 118	6
+ 702	5
+ 758	8
+ 105	6
+ 913	10
+ 395	3
+ 45	7
+ 204	2
+ 433	1
+ 329	6
+ 939	4
+ 764	1
+ 48	8
+ 650	10
+ 542	5
+ 610	7
+ 141	3
+ 126	9
+ 146	2
+ 525	1
+ 208	9
+ 409	3
+ 584	6
+ 474	0
+ 710	8
+ 654	6
+ 190	4
+ 770	2
+ 247	4
+ 198	8
+ 968	8
+ 448	1
+ 121	6
+ 8	3
+ 805	5
+ 326	0
+ 452	7
+ 265	0
+ 347	7
+ 53	1
+ 542	7
+ 706	7
+ 124	5
+ 970	4
+ 896	2
+ 159	9
+ 977	6
+ 972	1
+ 182	10
+ 364	10
+ 513	7
+ 999	10
+ 425	3
+ 1000	8
+ 3	1
+ 829	5
+ 759	5
+ 277	9
+ 12	2
+ 254	9
+ 415	4
+ 772	4
+ 21	7
+ 490	2
+ 725	9
+ 189	2
+ 544	2
+ 202	10
+ 452	2
+ 741	5
+ 254	6
+ 1000	0
+ 106	3
+ 896	1
+ 523	1
+ 27	9
+ 563	8
+ 330	6
+ 544	8
+ 786	3
+ 674	10
+ 506	2
+ 162	7
+ 186	6
+ 910	9
+ 69	2
+ 496	1
+ 177	6
+ 346	1
+ 720	9
+ 223	7
+ 807	8
+ 546	1
+ 369	1
+ 958	2
+ 358	6
+ 129	9
+ 849	3
+ 573	0
+ 906	5
+ 961	10
+ 646	5
+ 45	8
+ 59	4
+ 896	8
+ 259	1
+ 526	1
+ 904	1
+ 204	3
+ 162	2
+ 428	5
+ 793	6
+ 385	6
+ 849	10
+ 676	8
+ 440	6
+ 731	1
+ 94	8
+ 909	2
+ 166	8
+ 933	4
+ 923	5
+ 492	8
+ 531	7
+ 100	7
+ 858	5
+ 214	7
+ 86	6
+ 292	9
+ 556	10
+ 691	10
+ 604	4
+ 82	7
+ 197	10
+ 851	4
+ 796	8
+ 788	7
+ 243	3
+ 547	8
+ 975	6
+ 467	8
+ 176	7
+ 484	3
+ 279	8
+ 198	8
+ 743	9
+ 832	3
+ 310	9
+ 46	5
+ 906	9
+ 871	7
+ 681	7
+ 422	9
+ 938	10
+ 698	9
+ 615	2
+ 747	8
+ 846	2
+ 53	1
+ 6	3
+ 961	7
+ 139	8
+ 97	4
+ 707	1
+ 957	6
+ 40	8
+ 314	7
+ 487	7
+ 645	4
+ 704	3
+ 339	3
+ 508	1
+ 110	4
+ 315	2
+ 479	3
+ 414	4
+ 70	6
+ 231	2
+ 3	9
+ 311	10
+ 550	4
+ 788	9
+ 72	3
+ 600	7
+ 700	3
+ 60	0
+ 623	6
+ 124	7
+ 922	4
+ 897	4
+ 760	3
+ 839	8
+ 864	1
+ 998	9
+ 9	3
+ 827	6
+ 660	6
+ 423	7
+ 891	0
+ 450	6
+ 327	5
+ 630	10
+ 78	8
+ 685	0
+ 194	6
+ 401	10
+ 893	2
+ 785	8
+ 311	8
+ 625	3
+ 92	5
+ 878	8
+ 68	3
+ 484	10
+ 325	9
+ 550	7
+ 444	2
+ 603	5
+ 935	3
+ 522	1
+ 870	9
+ 82	8
+ 163	9
+ 521	5
+ 650	1
+ 794	7
+ 598	7
+ 494	7
+ 974	10
+ 625	3
+ 911	2
+ 951	4
+ 356	6
+ 877	3
+ 842	4
+ 419	7
+ 322	5
+ 476	5
+ 369	10
+ 960	0
+ 143	8
+ 761	7
+ 426	3
+ 408	4
+ 233	0
+ 698	1
+ 209	6
+ 499	6
+ 203	4
+ 856	0
+ 775	3
+ 757	1
+ 776	2
+ 583	1
+ 229	5
+ 164	4
+ 297	9
+ 114	7
+ 180	5
+ 122	4
+ 555	8
+ 556	8
+ 469	1
+ 328	7
+ 431	2
+ 717	2
+ 459	5
+ 302	2
+ 706	9
+ 380	9
+ 428	5
+ 308	7
+ 468	4
+ 447	6
+ 944	6
+ 60	5
+ 390	6
+ 262	9
+ 672	6
+ 531	1
+ 774	2
+ 307	2
+ 721	6
+ 468	4
+ 495	8
+ 363	9
+ 392	7
+ 648	9
+ 93	1
+ 508	0
+ 664	6
+ 536	1
+ 185	8
+ 913	9
+ 390	4
+ 959	2
+ 692	3
+ 397	4
+ 877	9
+ 841	4
+ 713	2
+ 295	1
+ 875	9
+ 965	10
+ 37	5
+ 6	7
+ 42	5
+ 755	2
+ 342	7
+ 84	7
+ 112	0
+ 895	8
+ 310	3
+ 218	2
+ 158	1
+ 559	9
+ 264	9
+ 976	1
+ 796	9
+ 108	8
+ 413	1
+ 533	5
+ 658	3
+ 682	10
+ 956	8
+ 731	1
+ 809	6
+ 873	1
+ 918	1
+ 307	1
+ 152	9
+ 947	4
+ 719	9
+ 555	5
+ 863	7
+ 347	3
+ 778	9
+ 731	4
+ 168	4
+ 435	1
+ 179	2
+ 193	10
+ 791	1
+ 108	7
+ 159	4
+ 786	3
+ 280	7
+ 726	10
+ 655	3
+ 512	5
+ 944	9
+ 793	7
+ 739	5
+ 158	9
+ 937	6
+ 32	1
+ 758	2
+ 104	5
+ 292	2
+ 259	5
+ 626	0
+ 761	9
+ 777	5
+ 903	4
+ 767	4
+ 949	7
+ 274	7
+ 434	0
+ 266	6
+ 921	2
+ 184	10
+ 318	9
+ 178	4
+ 491	5
+ 633	8
+ 921	3
+ 795	7
+ 164	6
+ 168	1
+ 3	9
+ 483	10
+ 647	8
+ 694	1
+ 771	10
+ 673	7
+ 163	9
+ 644	5
+ 799	8
+ 903	3
+ 292	5
+ 40	2
+ 794	8
+ 895	10
+ 407	1
+ 25	4
+ 999	5
+ 362	6
+ 265	1
+ 727	0
+ 16	4
+ 727	2
+ 257	4
+ 660	1
+ 193	6
+ 345	5
+ 98	4
+ 698	9
+ 221	6
+ 850	6
+ 656	9
+ 37	7
+ 383	4
+ 301	6
+ 455	0
+ 684	5
+ 428	4
+ 650	7
+ 781	3
+ 740	10
+ 872	1
+ 459	10
+ 471	2
+ 863	7
+ 749	7
+ 319	4
+ 589	4
+ 59	10
+ 755	4
+ 621	2
+ 388	3
+ 681	8
+ 716	3
+ 501	5
+ 641	2
+ 471	5
+ 327	9
+ 484	8
+ 87	3
+ 490	8
+ 60	8
+ 241	6
+ 166	3
+ 622	9
+ 661	2
+ 132	0
+ 547	8
+ 865	3
+ 144	4
+ 760	8
+ 606	2
+ 299	9
+ 162	8
+ 731	2
+ 130	2
+ 85	2
+ 30	3
+ 840	2
+ 627	5
+ 117	3
+ 705	2
+ 337	3
+ 61	2
+ 515	2
+ 566	3
+ 991	2
+ 506	3
+ 105	7
+ 74	8
+ 916	2
+ 57	0
+ 395	1
+ 328	2
+ 282	10
+ 697	4
+ 243	4
+ 647	6
+ 654	7
+ 781	2
+ 914	3
+ 444	9
+ 520	9
+ 196	6
+ 618	3
+ 461	5
+ 474	5
+ 535	9
+ 604	9
+ 105	9
+ 818	8
+ 285	1
+ 205	9
+ 641	9
+ 641	4
+ 28	6
+ 768	5
+ 461	3
+ 421	7
+ 913	0
+ 927	4
+ 573	4
+ 892	1
+ 271	5
+ 971	4
+ 382	8
+ 179	7
+ 851	4
+ 600	5
+ 243	2
+ 913	3
+ 796	7
+ 742	3
+ 969	2
+ 914	9
+ 202	8
+ 257	8
+ 243	1
+ 882	5
+ 644	9
+ 891	0
+ 643	1
+ 694	5
+ 454	3
+ 986	7
+ 535	9
+ 967	3
+ 580	7
+ 588	5
+ 871	5
+ 432	1
+ 344	7
+ 847	6
+ 837	7
+ 100	5
+ 583	10
+ 508	2
+ 61	2
+ 721	5
+ 497	7
+ 211	0
+ 606	2
+ 363	2
+ 887	10
+ 736	8
+ 454	2
+ 831	8
+ 858	7
+ 384	7
+ 408	5
+ 176	10
+ 475	7
+ 218	5
+ 887	9
+ 51	4
+ 646	3
+ 415	3
+ 440	8
+ 438	3
+ 729	2
+ 86	2
+ 344	9
+ 981	2
+ 596	4
+ 896	0
+ 850	1
+ 995	3
+ 756	2
+ 861	6
+ 152	9
+ 26	8
+ 174	4
+ 50	6
+ 218	5
+ 942	9
+ 663	0
+ 131	0
+ 944	1
+ 208	5
+ 477	1
+ 544	3
+ 176	5
+ 652	9
+ 752	5
+ 574	9
+ 424	6
+ 702	6
+ 41	8
+ 212	3
+ 241	2
+ 207	9
+ 181	3
+ 911	1
+ 450	1
+ 665	9
+ 222	2
+ 254	4
+ 748	9
+ 329	5
+ 418	9
+ 405	8
+ 504	1
+ 441	5
+ 860	7
+ 803	1
+ 807	0
+ 4	10
+ 348	9
+ 114	8
+ 33	8
+ 725	3
+ 988	10
+ 653	7
+ 885	10
+ 238	3
+ 886	6
+ 146	4
+ 751	6
+ 934	6
+ 240	7
+ 712	0
+ 748	7
+ 35	1
+ 631	1
+ 894	7
+ 928	6
+ 920	9
+ 598	6
+ 654	5
+ 556	9
+ 786	4
+ 535	9
+ 832	3
+ 518	8
+ 896	8
+ 504	6
+ 804	3
+ 324	8
+ 347	10
+ 989	2
+ 619	9
+ 860	5
+ 834	5
+ 113	5
+ 942	7
+ 380	7
+ 112	9
+ 659	9
+ 200	2
+ 711	1
+ 934	2
+ 704	7
+ 466	0
+ 578	8
+ 983	6
+ 55	6
+ 485	9
+ 142	3
+ 373	3
+ 808	3
+ 925	2
+ 43	0
+ 102	7
+ 981	3
+ 878	7
+ 398	8
+ 906	1
+ 551	4
+ 129	1
+ 186	1
+ 697	2
+ 715	2
+ 156	9
+ 502	5
+ 112	3
+ 844	0
+ 497	9
+ 74	6
+ 589	1
+ 900	5
+ 747	3
+ 280	7
+ 399	8
+ 26	5
+ 961	2
+ 641	7
+ 453	4
+ 840	6
+ 212	3
+ 138	3
+ 651	10
+ 362	1
+ 869	4
+ 746	5
+ 490	6
+ 925	2
+ 943	2
+ 890	3
+ 36	9
+ 870	10
+ 128	5
+ 655	6
+ 866	5
+ 190	1
+ 837	3
+ 403	5
+ 310	8
+ 636	2
+ 200	4
+ 637	7
+ 28	6
+ 927	10
+ 766	8
+ 313	8
+ 733	2
+ 798	9
+ 695	5
+ 443	6
+ 948	6
+ 640	8
+ 960	0
+ 274	3
+ 808	9
+ 449	0
+ 292	1
+ 698	3
+ 648	6
+ 291	4
+ 443	6
+ 215	2
+ 788	0
+ 37	5
+ 467	5
+ 44	4
+ 112	7
+ 200	1
+ 727	5
+ 342	5
+ 383	8
+ 542	7
+ 877	2
+ 995	5
+ 866	3
+ 938	3
+ 891	2
+ 484	7
+ 167	5
+ 162	6
+ 1	2
+ 48	1
+ 890	2
+ 186	6
+ 721	5
+ 151	1
+ 318	7
+ 779	2
+ 934	8
+ 719	8
+ 61	7
+ 108	10
+ 810	6
+ 632	10
+ 114	8
+ 610	1
+ 0	7
+ 229	9
+ 906	4
+ 506	6
+ 942	7
+ 731	3
+ 350	5
+ 455	3
+ 284	2
+ 83	3
+ 830	2
+ 297	6
+ 783	9
+ 617	9
+ 723	2
+ 12	7
+ 885	2
+ 614	8
+ 656	1
+ 418	6
+ 777	1
+ 858	1
+ 659	3
+ 411	9
+ 486	5
+ 288	3
+ 685	6
+ 957	5
+ 514	6
+ 365	2
+ 801	4
+ 961	7
+ 618	6
+ 477	3
+ 695	9
+ 871	5
+ 44	7
+ 600	7
+ 42	0
+ 646	5
+ 504	9
+ 845	2
+ 520	8
+ 657	0
+ 375	0
+ 272	2
+ 398	2
+ 862	0
+ 809	3
+ 290	5
+ 234	2
+ 976	3
+ 891	6
+ 982	9
+ 587	6
+ 461	1
+ 563	3
+ 280	1
+ 107	9
+ 117	5
+ 958	4
+ 658	4
+ 623	5
+ 372	4
+ 859	7
+ 935	1
+ 823	9
+ 372	7
+ 488	4
+ 646	1
+ 982	1
+ 165	5
+ 412	4
+ 628	5
+ 382	7
+ 2	3
+ 135	7
+ 696	8
+ 179	1
+ 190	0
+ 730	1
+ 131	6
+ 36	5
+ 266	5
+ 857	9
+ 598	8
+ 19	8
+ 384	4
+ 209	0
+ 951	6
+ 759	10
+ 932	9
+ 612	6
+ 652	8
+ 696	8
+ 830	4
+ 966	10
+ 978	0
+ 464	2
+ 527	3
+ 155	1
+ 160	2
+ 889	5
+ 605	1
+ 556	6
+ 690	3
+ 508	6
+ 209	1
+ 249	9
+ 912	9
+ 703	7
+ 370	7
+ 703	3
+ 672	2
+ 591	2
+ 488	7
+ 324	6
+ 921	2
+ 191	5
+ 311	7
+ 82	0
+ 62	6
+ 621	3
+ 710	9
+ 132	6
+ 815	8
+ 364	2
+ 504	1
+ 533	2
+ 234	1
+ 374	7
+ 872	7
+ 369	8
+ 911	6
+ 319	2
+ 307	4
+ 221	4
+ 990	8
+ 641	7
+ 713	8
+ 323	5
+ 608	7
+ 714	1
+ 755	2
+ 288	10
+ 372	7
+ 711	2
+ 360	1
+ 36	3
+ 640	4
+ 492	9
+ 755	7
+ 317	7
+ 556	10
+ 446	3
+ 731	8
+ 798	3
+ 457	5
+ 450	2
+ 760	7
+ 201	1
+ 400	9
+ 375	8
+ 991	4
+ 31	6
+ 765	5
+ 578	5
+ 237	9
+ 265	8
+ 852	7
+ 63	6
+ 481	9
+ 921	9
+ 374	4
+ 149	1
+ 109	3
+ 265	5
+ 261	6
+ 270	3
+ 50	3
+ 883	8
+ 824	5
+ 335	1
+ 355	6
+ 854	2
+ 312	9
+ 789	8
+ 778	7
+ 730	2
+ 81	9
+ 285	2
+ 228	6
+ 700	5
+ 190	10
+ 740	2
+ 271	6
+ 55	1
+ 84	4
+ 156	4
+ 990	0
+ 646	3
+ 927	4
+ 94	7
+ 145	8
+ 856	2
+ 702	1
+ 417	9
+ 692	1
+ 418	9
+ 87	2
+ 122	4
+ 782	2
+ 453	9
+ 567	6
+ 305	6
+ 619	10
+ 859	5
+ 386	10
+ 250	5
+ 776	1
+ 757	5
+ 249	2
+ 407	9
+ 291	8
+ 823	4
+ 983	9
+ 736	8
+ 121	2
+ 631	7
+ 798	9
+ 245	4
+ 886	1
+ 963	3
+ 57	2
+ 803	8
+ 320	6
+ 310	6
+ 734	7
+ 509	0
+ 543	3
+ 403	5
+ 276	1
+ 291	4
+ 328	9
+ 85	1
+ 858	3
+ 544	7
+ 434	5
+ 16	5
+ 719	8
+ 324	0
+ 378	6
+ 607	1
+ 352	1
+ 137	9
+ 447	5
+ 420	7
+ 679	7
+ 119	0
+ 634	2
+ 134	5
+ 535	7
+ 236	10
+ 184	3
+ 461	9
+ 71	8
+ 942	4
+ 418	5
+ 561	8
+ 664	7
+ 664	1
+ 238	1
+ 834	9
+ 796	10
+ 924	4
+ 158	1
+ 921	7
+ 735	2
+ 662	9
+ 409	1
+ 822	5
+ 907	8
+ 929	3
+ 312	5
+ 95	10
+ 188	8
+ 87	4
+ 844	9
+ 342	6
+ 874	3
+ 69	0
+ 324	10
+ 724	1
+ 148	4
+ 977	6
+ 510	8
+ 38	4
+ 563	10
+ 743	9
+ 458	8
+ 851	6
+ 598	9
+ 72	4
+ 859	4
+ 81	7
+ 680	2
+ 764	0
+ 141	5
+ 63	3
+ 875	0
+ 846	4
+ 839	9
+ 801	4
+ 851	5
+ 277	3
+ 382	1
+ 955	10
+ 65	0
+ 421	9
+ 441	5
+ 656	1
+ 653	4
+ 125	8
+ 908	2
+ 83	8
+ 228	9
+ 167	1
+ 813	10
+ 469	7
+ 513	7
+ 974	9
+ 873	9
+ 875	9
+ 955	3
+ 862	4
+ 799	5
+ 517	5
+ 939	6
+ 245	8
+ 830	3
+ 630	1
+ 257	8
+ 126	1
+ 765	6
+ 734	3
+ 341	7
+ 173	2
+ 637	0
+ 152	6
+ 344	0
+ 988	1
+ 533	5
+ 594	5
+ 148	8
+ 319	10
+ 166	9
+ 37	4
+ 745	2
+ 493	5
+ 757	2
+ 788	1
+ 935	10
+ 311	6
+ 9	5
+ 164	4
+ 478	2
+ 495	0
+ 658	1
+ 483	8
+ 927	8
+ 785	1
+ 751	8
+ 516	5
+ 984	0
+ 7	7
+ 235	8
+ 840	2
+ 756	2
+ 742	8
+ 615	9
+ 118	1
+ 58	6
+ 104	7
+ 700	6
+ 522	6
+ 389	3
+ 720	1
+ 128	2
+ 637	1
+ 244	6
+ 854	5
+ 439	7
+ 650	2
+ 845	4
+ 961	5
+ 298	1
+ 552	4
+ 690	7
+ 72	4
+ 243	6
+ 18	6
+ 901	7
+ 772	0
+ 973	4
+ 142	2
+ 52	10
+ 695	5
+ 691	3
+ 687	5
+ 737	6
+ 995	0
+ 725	5
+ 392	4
+ 203	5
+ 806	4
+ 59	8
+ 77	10
+ 562	8
+ 989	5
+ 258	1
+ 751	3
+ 127	4
+ 802	8
+ 792	5
+ 353	5
+ 136	3
+ 564	9
+ 895	10
+ 278	1
+ 420	1
+ 544	5
+ 908	6
+ 438	5
+ 471	4
+ 5	7
+ 558	8
+ 40	7
+ 203	8
+ 503	10
+ 331	9
+ 523	5
+ 205	1
+ 330	1
+ 42	6
+ 199	5
+ 692	7
+ 941	6
+ 363	4
+ 70	8
+ 806	1
+ 563	4
+ 831	6
+ 49	0
+ 445	6
+ 28	8
+ 408	6
+ 245	6
+ 638	6
+ 713	7
+ 182	9
+ 142	9
+ 654	1
+ 473	0
+ 463	5
+ 852	3
+ 619	4
+ 632	4
+ 18	7
+ 484	5
+ 233	5
+ 240	6
+ 63	5
+ 254	7
+ 59	10
+ 380	2
+ 880	5
+ 114	5
+ 606	6
+ 551	1
+ 131	4
+ 337	7
+ 818	10
+ 199	8
+ 650	7
+ 299	9
+ 195	5
+ 524	3
+ 23	8
+ 958	1
+ 746	3
+ 322	6
+ 860	4
+ 159	5
+ 23	7
+ 535	2
+ 114	9
+ 904	9
+ 842	1
+ 767	5
+ 786	1
+ 375	10
+ 604	9
+ 239	6
+ 678	2
+ 708	4
+ 534	0
+ 49	4
+ 466	2
+ 861	5
+ 919	4
+ 643	0
+ 269	5
+ 964	1
+ 650	7
+ 603	4
+ 797	10
+ 418	4
+ 877	7
+ 28	6
+ 853	7
+ 979	4
+ 766	0
+ 782	2
+ 236	6
+ 721	2
+ 40	4
+ 188	3
+ 911	2
+ 419	6
+ 884	0
+ 999	7
+ 1000	4
+ 82	9
+ 73	1
+ 432	9
+ 846	4
+ 313	6
+ 439	1
+ 844	7
+ 739	6
+ 831	8
+ 929	0
+ 87	8
+ 172	5
+ 402	1
+ 528	4
+ 736	5
+ 817	8
+ 405	9
+ 927	8
+ 817	8
+ 249	1
+ 384	7
+ 225	2
+ 364	10
+ 793	2
+ 742	7
+ 215	8
+ 562	4
+ 336	10
+ 443	9
+ 365	2
+ 392	2
+ 997	8
+ 72	9
+ 635	9
+ 697	9
+ 19	1
+ 573	2
+ 309	9
+ 208	1
+ 132	10
+ 824	3
+ 780	4
+ 734	1
+ 351	2
+ 980	7
+ 356	4
+ 897	4
+ 170	10
+ 276	8
+ 858	10
+ 690	9
+ 54	3
+ 121	4
+ 199	3
+ 466	3
+ 280	3
+ 678	1
+ 677	4
+ 175	0
+ 589	2
+ 743	9
+ 527	6
+ 297	7
+ 610	6
+ 502	5
+ 547	2
+ 345	6
+ 454	5
+ 965	7
+ 795	4
+ 983	1
+ 721	7
+ 135	4
+ 74	3
+ 425	7
+ 465	2
+ 607	10
+ 808	9
+ 689	4
+ 478	2
+ 886	0
+ 382	2
+ 626	8
+ 697	6
+ 488	5
+ 21	5
+ 567	7
+ 133	7
+ 140	2
+ 12	6
+ 869	5
+ 734	5
+ 469	5
+ 381	2
+ 960	9
+ 349	8
+ 884	7
+ 77	5
+ 567	8
+ 100	1
+ 266	1
+ 527	8
+ 864	7
+ 535	0
+ 867	5
+ 570	7
+ 24	3
+ 213	5
+ 845	6
+ 651	8
+ 453	0
+ 651	3
+ 732	7
+ 846	3
+ 501	9
+ 355	8
+ 67	9
+ 600	9
+ 542	1
+ 935	4
+ 682	5
+ 146	7
+ 808	4
+ 199	7
+ 953	9
+ 459	4
+ 851	1
+ 743	6
+ 837	6
+ 882	3
+ 534	2
+ 105	6
+ 118	7
+ 532	7
+ 840	5
+ 70	5
+ 971	2
+ 228	8
+ 575	4
+ 433	5
+ 277	9
+ 935	1
+ 1	7
+ 710	8
+ 266	6
+ 176	8
+ 828	3
+ 402	9
+ 986	9
+ 607	8
+ 399	7
+ 348	4
+ 892	6
+ 150	5
+ 1	6
+ 996	3
+ 474	9
+ 406	5
+ 609	1
+ 312	9
+ 708	5
+ 676	5
+ 768	1
+ 483	8
+ 10	1
+ 580	4
+ 766	9
+ 780	7
+ 502	9
+ 125	5
+ 513	1
+ 782	10
+ 52	2
+ 461	7
+ 304	8
+ 535	0
+ 261	2
+ 548	0
+ 288	0
+ 783	3
+ 121	4
+ 708	9
+ 290	5
+ 545	8
+ 418	7
+ 296	9
+ 791	1
+ 918	8
+ 266	4
+ 504	6
+ 153	0
+ 581	4
+ 250	1
+ 443	5
+ 161	2
+ 836	3
+ 589	5
+ 169	9
+ 32	7
+ 671	4
+ 384	10
+ 381	2
+ 45	3
+ 19	3
+ 678	5
+ 881	8
+ 561	5
+ 244	8
+ 591	7
+ 349	8
+ 913	2
+ 34	5
+ 728	2
+ 380	8
+ 916	1
+ 209	3
+ 18	6
+ 476	1
+ 890	5
+ 374	6
+ 17	3
+ 399	6
+ 716	6
+ 389	3
+ 330	7
+ 60	2
+ 922	1
+ 744	6
+ 296	1
+ 409	2
+ 174	6
+ 513	2
+ 209	10
+ 255	1
+ 483	6
+ 667	5
+ 883	1
+ 78	6
+ 708	5
+ 907	0
+ 204	10
+ 280	1
+ 60	0
+ 775	4
+ 147	2
+ 569	3
+ 803	1
+ 512	0
+ 71	8
+ 111	6
+ 396	8
+ 53	3
+ 842	1
+ 878	6
+ 597	8
+ 588	8
+ 751	9
+ 927	8
+ 891	7
+ 169	0
+ 886	7
+ 359	7
+ 820	9
+ 701	9
+ 638	8
+ 445	0
+ 588	5
+ 312	4
+ 628	2
+ 981	2
+ 975	6
+ 26	7
+ 437	10
+ 538	3
+ 655	7
+ 366	5
+ 445	7
+ 229	3
+ 595	9
+ 156	2
+ 741	6
+ 266	3
+ 98	6
+ 760	7
+ 768	7
+ 952	7
+ 310	10
+ 469	7
+ 931	0
+ 74	6
+ 713	4
+ 127	2
+ 164	4
+ 423	8
+ 286	6
+ 992	0
+ 180	3
+ 357	3
+ 837	1
+ 5	6
+ 858	10
+ 348	2
+ 935	8
+ 915	9
+ 823	10
+ 452	5
+ 429	6
+ 694	6
+ 935	1
+ 352	2
+ 696	3
+ 249	9
+ 602	6
+ 153	4
+ 722	2
+ 44	6
+ 115	4
+ 748	0
+ 208	7
+ 915	0
+ 652	4
+ 566	1
+ 946	3
+ 673	9
+ 377	0
+ 102	1
+ 369	4
+ 948	10
+ 956	1
+ 409	7
+ 259	5
+ 258	4
+ 844	0
+ 423	1
+ 669	3
+ 82	3
+ 705	6
+ 402	7
+ 908	1
+ 533	3
+ 101	6
+ 357	5
+ 986	3
+ 440	9
+ 406	8
+ 620	7
+ 303	9
+ 39	1
+ 885	5
+ 199	6
+ 801	3
+ 875	5
+ 929	3
+ 157	8
+ 353	7
+ 123	5
+ 325	5
+ 923	3
+ 785	4
+ 252	2
+ 213	9
+ 857	5
+ 751	9
+ 663	6
+ 359	9
+ 190	2
+ 142	1
+ 665	1
+ 343	8
+ 909	7
+ 513	0
+ 149	8
+ 513	1
+ 148	3
+ 435	4
+ 489	6
+ 273	3
+ 163	0
+ 243	8
+ 660	6
+ 687	8
+ 761	8
+ 914	4
+ 901	3
+ 249	8
+ 952	8
+ 843	1
+ 600	4
+ 173	7
+ 653	6
+ 149	1
+ 255	4
+ 489	4
+ 446	7
+ 244	1
+ 334	9
+ 955	1
+ 760	9
+ 521	7
+ 126	8
+ 471	1
+ 532	3
+ 180	1
+ 668	4
+ 880	3
+ 961	0
+ 464	2
+ 450	10
+ 634	9
+ 685	9
+ 2	0
+ 809	10
+ 113	6
+ 826	6
+ 230	10
+ 405	7
+ 30	9
+ 14	2
+ 69	7
+ 563	9
+ 3	5
+ 978	5
+ 740	4
+ 420	4
+ 324	1
+ 252	3
+ 123	1
+ 283	2
+ 631	1
+ 871	9
+ 60	3
+ 561	1
+ 213	6
+ 301	3
+ 257	9
+ 232	3
+ 388	2
+ 727	1
+ 637	1
+ 501	10
+ 252	8
+ 288	4
+ 815	6
+ 612	4
+ 678	5
+ 306	7
+ 759	9
+ 829	10
+ 442	1
+ 255	7
+ 994	5
+ 959	4
+ 696	7
+ 509	3
+ 833	0
+ 294	1
+ 764	6
+ 461	6
+ 152	1
+ 25	8
+ 555	3
+ 569	3
+ 199	4
+ 287	6
+ 528	5
+ 339	5
+ 28	3
+ 903	7
+ 983	4
+ 57	8
+ 422	4
+ 902	2
+ 933	4
+ 765	1
+ 435	8
+ 915	10
+ 122	5
+ 304	3
+ 882	6
+ 961	4
+ 133	3
+ 931	2
+ 598	8
+ 885	6
+ 246	9
+ 397	7
+ 292	3
+ 853	2
+ 662	6
+ 310	1
+ 409	2
+ 86	5
+ 709	4
+ 852	6
+ 982	8
+ 1	1
+ 114	9
+ 276	7
+ 766	2
+ 293	0
+ 102	7
+ 680	4
+ 989	5
+ 620	7
+ 152	9
+ 747	6
+ 154	8
+ 92	9
+ 224	9
+ 454	2
+ 758	5
+ 321	9
+ 386	6
+ 584	2
+ 758	9
+ 164	9
+ 567	8
+ 255	6
+ 377	9
+ 207	5
+ 804	10
+ 89	10
+ 788	2
+ 821	0
+ 126	3
+ 218	9
+ 729	5
+ 757	1
+ 136	3
+ 267	9
+ 219	4
+ 755	8
+ 275	0
+ 342	7
+ 885	5
+ 179	7
+ 503	3
+ 648	3
+ 450	5
+ 303	6
+ 743	5
+ 460	5
+ 60	2
+ 587	2
+ 559	9
+ 91	8
+ 285	8
+ 563	6
+ 856	9
+ 211	7
+ 454	4
+ 430	10
+ 659	1
+ 249	1
+ 546	6
+ 685	3
+ 72	1
+ 762	1
+ 363	3
+ 328	9
+ 202	4
+ 699	5
+ 265	3
+ 47	1
+ 168	3
+ 862	6
+ 649	3
+ 580	3
+ 369	8
+ 417	9
+ 379	1
+ 205	5
+ 247	10
+ 583	6
+ 315	9
+ 532	5
+ 331	2
+ 5	6
+ 493	1
+ 717	7
+ 310	6
+ 283	10
+ 870	9
+ 267	2
+ 691	7
+ 154	1
+ 786	4
+ 522	0
+ 326	1
+ 642	6
+ 17	2
+ 158	3
+ 405	2
+ 943	9
+ 215	7
+ 559	5
+ 238	8
+ 484	1
+ 704	8
+ 346	4
+ 435	5
+ 465	2
+ 860	10
+ 253	2
+ 92	9
+ 826	1
+ 70	10
+ 456	5
+ 147	4
+ 373	4
+ 60	9
+ 887	3
+ 774	4
+ 405	5
+ 122	8
+ 873	6
+ 253	3
+ 778	1
+ 326	0
+ 298	4
+ 927	1
+ 527	10
+ 109	10
+ 471	3
+ 383	8
+ 618	4
+ 775	5
+ 740	5
+ 875	1
+ 27	10
+ 897	9
+ 554	1
+ 239	3
+ 263	6
+ 362	6
+ 982	3
+ 686	5
+ 285	8
+ 492	8
+ 51	9
+ 600	7
+ 317	4
+ 173	1
+ 924	0
+ 203	10
+ 45	1
+ 851	6
+ 250	1
+ 930	5
+ 654	3
+ 74	6
+ 581	8
+ 145	9
+ 554	6
+ 623	6
+ 511	2
+ 274	8
+ 598	4
+ 886	5
+ 496	1
+ 474	5
+ 189	3
+ 141	4
+ 414	1
+ 953	1
+ 363	0
+ 704	9
+ 786	8
+ 811	3
+ 485	4
+ 946	10
+ 657	2
+ 825	3
+ 668	7
+ 778	2
+ 800	3
+ 705	10
+ 576	8
+ 429	10
+ 916	4
+ 58	3
+ 409	8
+ 225	2
+ 610	0
+ 536	1
+ 470	5
+ 92	1
+ 702	9
+ 383	4
+ 628	2
+ 533	4
+ 412	2
+ 417	10
+ 84	8
+ 978	0
+ 229	0
+ 280	6
+ 798	5
+ 834	4
+ 540	4
+ 504	0
+ 852	6
+ 138	6
+ 512	5
+ 925	1
+ 682	5
+ 567	1
+ 696	10
+ 82	8
+ 830	1
+ 780	1
+ 96	1
+ 697	9
+ 565	5
+ 302	1
+ 900	8
+ 116	8
+ 401	3
+ 307	9
+ 774	2
+ 52	5
+ 690	6
+ 550	4
+ 603	6
+ 166	4
+ 691	9
+ 493	8
+ 7	2
+ 681	6
+ 720	10
+ 677	6
+ 789	8
+ 374	2
+ 46	7
+ 103	8
+ 913	2
+ 276	6
+ 774	8
+ 989	4
+ 457	2
+ 811	1
+ 102	3
+ 935	1
+ 493	6
+ 680	2
+ 601	4
+ 835	4
+ 149	2
+ 580	2
+ 889	7
+ 14	8
+ 838	3
+ 404	6
+ 115	4
+ 989	6
+ 548	8
+ 720	6
+ 103	7
+ 758	6
+ 272	4
+ 810	9
+ 795	6
+ 262	9
+ 852	8
+ 138	7
+ 525	2
+ 543	4
+ 442	9
+ 975	6
+ 340	10
+ 129	9
+ 764	8
+ 537	9
+ 504	3
+ 463	8
+ 733	3
+ 649	5
+ 916	9
+ 471	8
+ 754	6
+ 510	3
+ 761	1
+ 642	2
+ 1000	6
+ 761	3
+ 581	9
+ 227	3
+ 740	8
+ 211	2
+ 58	7
+ 21	8
+ 946	7
+ 318	9
+ 582	8
+ 631	3
+ 398	1
+ 615	2
+ 194	3
+ 363	2
+ 875	1
+ 533	5
+ 16	8
+ 801	8
+ 522	0
+ 0	6
+ 686	0
+ 371	6
+ 692	7
+ 494	3
+ 478	1
+ 610	9
+ 266	2
+ 36	5
+ 483	4
+ 653	4
+ 524	2
+ 814	5
+ 945	6
+ 296	5
+ 627	3
+ 47	3
+ 318	4
+ 944	0
+ 108	4
+ 283	6
+ 564	9
+ 463	8
+ 118	5
+ 290	6
+ 898	9
+ 959	4
+ 129	8
+ 963	1
+ 388	3
+ 541	0
+ 555	6
+ 327	9
+ 6	3
+ 882	1
+ 711	2
+ 700	3
+ 58	2
+ 105	2
+ 662	4
+ 777	6
+ 338	7
+ 983	5
+ 509	9
+ 540	9
+ 205	1
+ 912	8
+ 669	2
+ 633	7
+ 511	5
+ 790	2
+ 680	5
+ 496	7
+ 653	6
+ 915	3
+ 995	7
+ 875	3
+ 429	9
+ 800	9
+ 804	3
+ 835	0
+ 422	7
+ 768	1
+ 987	4
+ 767	5
+ 915	6
+ 720	6
+ 47	2
+ 334	7
+ 817	2
+ 15	8
+ 941	9
+ 145	4
+ 747	9
+ 307	6
+ 286	1
+ 559	7
+ 890	3
+ 798	9
+ 727	6
+ 375	6
+ 122	1
+ 238	2
+ 311	6
+ 869	1
+ 820	9
+ 941	8
+ 773	1
+ 130	5
+ 31	4
+ 70	3
+ 580	6
+ 24	5
+ 956	8
+ 347	7
+ 387	7
+ 325	5
+ 817	6
+ 678	1
+ 134	5
+ 257	10
+ 431	2
+ 715	2
+ 284	8
+ 724	3
+ 281	8
+ 632	9
+ 423	7
+ 331	4
+ 477	7
+ 62	9
+ 400	4
+ 374	2
+ 950	1
+ 346	1
+ 599	6
+ 38	0
+ 800	8
+ 234	1
+ 597	10
+ 399	9
+ 751	0
+ 740	2
+ 686	1
+ 554	2
+ 749	6
+ 28	1
+ 2	4
+ 366	10
+ 454	7
+ 36	1
+ 314	1
+ 83	1
+ 827	3
+ 198	4
+ 275	6
+ 304	0
+ 628	0
+ 201	3
+ 114	8
+ 477	9
+ 370	5
+ 12	4
+ 907	4
+ 324	4
+ 89	4
+ 414	4
+ 435	5
+ 517	3
+ 815	7
+ 687	1
+ 313	10
+ 116	9
+ 34	3
+ 255	1
+ 71	7
+ 12	4
+ 237	0
+ 812	1
+ 401	1
+ 505	5
+ 496	9
+ 893	9
+ 417	4
+ 193	2
+ 125	9
+ 321	4
+ 871	4
+ 380	9
+ 753	6
+ 53	8
+ 366	1
+ 264	6
+ 88	1
+ 747	5
+ 213	3
+ 979	7
+ 171	9
+ 640	6
+ 281	8
+ 819	4
+ 714	1
+ 845	6
+ 577	2
+ 489	3
+ 859	5
+ 154	2
+ 607	4
+ 828	7
+ 495	6
+ 184	7
+ 827	2
+ 417	10
+ 34	1
+ 586	3
+ 890	4
+ 721	6
+ 545	6
+ 188	1
+ 791	7
+ 452	7
+ 219	6
+ 875	8
+ 25	7
+ 521	5
+ 279	7
+ 228	1
+ 868	6
+ 105	9
+ 701	7
+ 217	6
+ 96	9
+ 196	6
+ 505	4
+ 763	3
+ 61	2
+ 946	3
+ 823	8
+ 107	8
+ 525	6
+ 368	8
+ 333	6
+ 910	2
+ 240	0
+ 103	9
+ 706	3
+ 533	8
+ 258	7
+ 443	8
+ 112	2
+ 58	2
+ 423	0
+ 455	2
+ 825	6
+ 93	4
+ 190	5
+ 154	5
+ 56	1
+ 725	3
+ 79	8
+ 237	8
+ 147	8
+ 587	4
+ 498	0
+ 167	6
+ 236	2
+ 785	7
+ 230	2
+ 904	1
+ 801	10
+ 405	10
+ 458	6
+ 515	5
+ 623	2
+ 810	7
+ 67	0
+ 486	2
+ 817	1
+ 619	3
+ 102	8
+ 926	3
+ 11	7
+ 998	2
+ 950	9
+ 297	8
+ 899	7
+ 743	4
+ 261	3
+ 871	9
+ 498	7
+ 585	6
+ 728	1
+ 779	5
+ 144	4
+ 861	2
+ 183	8
+ 585	2
+ 498	6
+ 436	4
+ 485	7
+ 200	4
+ 434	9
+ 741	7
+ 202	6
+ 578	7
+ 293	2
+ 264	0
+ 234	0
+ 566	4
+ 440	4
+ 624	6
+ 213	2
+ 817	7
+ 791	3
+ 160	3
+ 984	4
+ 660	4
+ 303	4
+ 113	5
+ 13	7
+ 204	3
+ 855	5
+ 326	1
+ 511	9
+ 467	10
+ 318	1
+ 573	5
+ 300	4
+ 242	1
+ 642	4
+ 367	6
+ 762	0
+ 45	1
+ 428	2
+ 570	4
+ 851	8
+ 746	7
+ 243	1
+ 795	8
+ 963	3
+ 705	3
+ 354	3
+ 811	7
+ 668	1
+ 744	3
+ 456	1
+ 937	2
+ 137	10
+ 283	6
+ 140	9
+ 5	10
+ 628	8
+ 697	9
+ 823	5
+ 626	8
+ 755	3
+ 66	1
+ 609	9
+ 762	3
+ 931	5
+ 586	4
+ 616	5
+ 604	8
+ 505	9
+ 318	6
+ 741	3
+ 636	4
+ 74	3
+ 241	9
+ 825	9
+ 683	6
+ 197	7
+ 688	8
+ 626	5
+ 82	6
+ 956	7
+ 944	6
+ 192	5
+ 325	7
+ 436	6
+ 342	2
+ 965	10
+ 547	0
+ 312	8
+ 936	1
+ 654	6
+ 717	9
+ 368	4
+ 658	10
+ 855	7
+ 551	8
+ 409	5
+ 382	6
+ 44	7
+ 298	5
+ 349	6
+ 658	3
+ 619	2
+ 354	9
+ 992	3
+ 67	6
+ 909	8
+ 498	3
+ 188	2
+ 271	0
+ 895	8
+ 854	3
+ 318	2
+ 905	4
+ 943	2
+ 843	3
+ 843	5
+ 607	5
+ 705	10
+ 392	7
+ 251	5
+ 343	2
+ 242	8
+ 437	4
+ 995	7
+ 474	9
+ 530	3
+ 195	8
+ 565	1
+ 210	5
+ 303	1
+ 800	1
+ 553	4
+ 609	3
+ 368	0
+ 955	6
+ 460	3
+ 780	7
+ 138	2
+ 133	1
+ 926	6
+ 24	5
+ 935	2
+ 304	5
+ 318	5
+ 8	6
+ 568	8
+ 768	1
+ 216	4
+ 379	6
+ 377	3
+ 204	8
+ 631	10
+ 539	8
+ 202	7
+ 901	1
+ 279	9
+ 584	2
+ 143	9
+ 714	5
+ 403	7
+ 83	10
+ 530	9
+ 92	7
+ 228	5
+ 331	6
+ 804	5
+ 442	4
+ 520	10
+ 203	7
+ 652	1
+ 850	9
+ 29	4
+ 145	2
+ 323	9
+ 633	7
+ 581	7
+ 696	1
+ 567	8
+ 857	8
+ 259	2
+ 400	1
+ 723	8
+ 498	2
+ 822	7
+ 965	5
+ 804	8
+ 405	8
+ 249	6
+ 5	6
+ 409	6
+ 297	10
+ 354	10
+ 100	9
+ 782	10
+ 716	0
+ 146	1
+ 104	9
+ 958	6
+ 112	8
+ 302	1
+ 255	1
+ 892	7
+ 939	1
+ 211	9
+ 713	6
+ 582	0
+ 609	9
+ 4	7
+ 857	8
+ 667	6
+ 828	8
+ 690	9
+ 683	6
+ 533	8
+ 428	8
+ 873	7
+ 942	8
+ 344	9
+ 907	6
+ 825	6
+ 174	4
+ 630	8
+ 343	6
+ 492	2
+ 421	2
+ 774	2
+ 972	5
+ 180	7
+ 112	7
+ 450	5
+ 549	3
+ 224	5
+ 88	6
+ 372	10
+ 122	2
+ 614	3
+ 604	2
+ 77	9
+ 880	6
+ 148	3
+ 728	9
+ 550	7
+ 386	7
+ 354	5
+ 444	8
+ 38	10
+ 127	3
+ 484	2
+ 829	9
+ 209	10
+ 53	8
+ 245	7
+ 68	3
+ 605	9
+ 892	8
+ 249	6
+ 674	8
+ 319	1
+ 529	7
+ 558	10
+ 478	6
+ 967	6
+ 858	5
+ 820	7
+ 307	0
+ 637	4
+ 852	9
+ 19	9
+ 205	6
+ 869	1
+ 376	1
+ 717	1
+ 917	0
+ 111	4
+ 709	7
+ 420	2
+ 265	4
+ 792	1
+ 838	6
+ 809	1
+ 640	4
+ 506	5
+ 328	5
+ 414	5
+ 148	3
+ 630	5
+ 400	3
+ 576	3
+ 382	7
+ 764	1
+ 356	2
+ 279	6
+ 571	1
+ 743	4
+ 683	6
+ 554	3
+ 998	1
+ 816	3
+ 585	2
+ 858	7
+ 512	5
+ 258	9
+ 835	8
+ 230	2
+ 520	10
+ 308	9
+ 177	6
+ 497	7
+ 659	2
+ 157	3
+ 793	7
+ 665	8
+ 772	5
+ 116	4
+ 711	10
+ 90	2
+ 463	3
+ 136	3
+ 181	4
+ 514	7
+ 359	8
+ 577	5
+ 410	1
+ 285	1
+ 314	4
+ 411	1
+ 153	1
+ 897	9
+ 557	0
+ 281	3
+ 988	4
+ 492	5
+ 719	6
+ 748	9
+ 993	3
+ 601	4
+ 85	2
+ 889	5
+ 251	2
+ 564	6
+ 616	10
+ 672	8
+ 50	6
+ 694	6
+ 582	10
+ 875	6
+ 346	4
+ 21	1
+ 994	8
+ 964	10
+ 31	6
+ 340	1
+ 742	2
+ 610	10
+ 402	2
+ 559	0
+ 148	2
+ 786	2
+ 800	5
+ 805	4
+ 455	7
+ 952	8
+ 48	10
+ 866	0
+ 741	8
+ 29	8
+ 395	4
+ 887	1
+ 597	5
+ 132	10
+ 670	7
+ 17	8
+ 921	8
+ 17	7
+ 283	8
+ 103	7
+ 503	1
+ 541	6
+ 27	4
+ 592	8
+ 238	6
+ 539	6
+ 990	4
+ 771	6
+ 922	9
+ 586	6
+ 593	6
+ 411	5
+ 406	4
+ 235	7
+ 250	3
+ 428	8
+ 393	10
+ 303	4
+ 376	9
+ 188	6
+ 516	7
+ 246	5
+ 153	0
+ 93	1
+ 919	7
+ 667	5
+ 282	1
+ 27	7
+ 506	3
+ 378	8
+ 600	8
+ 509	10
+ 775	8
+ 414	2
+ 707	6
+ 765	2
+ 328	0
+ 729	5
+ 28	8
+ 556	9
+ 501	2
+ 460	8
+ 301	5
+ 471	8
+ 749	8
+ 563	3
+ 655	1
+ 342	4
+ 883	8
+ 582	6
+ 357	3
+ 813	7
+ 357	5
+ 166	4
+ 364	7
+ 333	9
+ 945	8
+ 649	2
+ 280	1
+ 53	0
+ 969	6
+ 377	6
+ 688	7
+ 55	6
+ 476	6
+ 161	8
+ 983	10
+ 519	3
+ 516	7
+ 726	9
+ 407	1
+ 745	4
+ 853	4
+ 598	1
+ 514	7
+ 161	5
+ 268	5
+ 107	10
+ 258	2
+ 527	7
+ 799	7
+ 567	8
+ 663	1
+ 123	2
+ 772	8
+ 59	2
+ 909	8
+ 532	8
+ 197	1
+ 894	7
+ 781	1
+ 193	0
+ 593	3
+ 5	9
+ 463	5
+ 585	3
+ 221	2
+ 45	9
+ 238	2
+ 63	0
+ 18	1
+ 189	9
+ 925	7
+ 688	1
+ 851	6
+ 833	6
+ 636	0
+ 681	2
+ 327	7
+ 80	8
+ 217	7
+ 53	4
+ 817	1
+ 322	1
+ 266	4
+ 66	3
+ 506	3
+ 210	4
+ 976	9
+ 554	8
+ 480	4
+ 458	1
+ 414	1
+ 345	7
+ 824	4
+ 532	0
+ 90	6
+ 480	9
+ 683	8
+ 963	9
+ 187	0
+ 234	7
+ 284	4
+ 124	3
+ 342	7
+ 87	8
+ 66	5
+ 938	5
+ 683	3
+ 221	5
+ 708	8
+ 548	8
+ 338	0
+ 706	0
+ 830	7
+ 971	0
+ 699	2
+ 709	10
+ 649	8
+ 244	10
+ 511	3
+ 813	6
+ 875	8
+ 57	6
+ 34	3
+ 66	7
+ 30	6
+ 541	4
+ 642	2
+ 390	5
+ 917	4
+ 487	6
+ 565	2
+ 600	2
+ 29	8
+ 205	5
+ 174	0
+ 118	0
+ 769	2
+ 608	8
+ 452	7
+ 545	5
+ 288	1
+ 851	9
+ 333	2
+ 401	3
+ 601	9
+ 867	2
+ 84	5
+ 380	1
+ 311	6
+ 654	5
+ 604	8
+ 535	4
+ 947	1
+ 176	4
+ 817	7
+ 881	1
+ 808	7
+ 34	1
+ 972	4
+ 392	6
+ 322	3
+ 740	4
+ 725	1
+ 520	0
+ 706	2
+ 521	3
+ 947	1
+ 683	9
+ 199	9
+ 292	0
+ 581	2
+ 120	4
+ 905	2
+ 529	9
+ 588	9
+ 450	9
+ 179	2
+ 318	9
+ 310	8
+ 941	0
+ 13	5
+ 325	10
+ 518	0
+ 853	7
+ 867	1
+ 732	4
+ 318	9
+ 836	2
+ 6	4
+ 100	6
+ 287	6
+ 505	5
+ 741	8
+ 370	1
+ 661	3
+ 67	7
+ 773	4
+ 633	3
+ 401	5
+ 7	3
+ 631	7
+ 716	9
+ 592	6
+ 173	6
+ 917	3
+ 192	2
+ 824	7
+ 670	6
+ 520	0
+ 616	2
+ 351	7
+ 854	1
+ 75	5
+ 414	5
+ 973	4
+ 744	6
+ 160	5
+ 554	8
+ 11	7
+ 349	9
+ 0	5
+ 132	8
+ 239	8
+ 389	8
+ 842	0
+ 941	2
+ 688	8
+ 317	8
+ 282	7
+ 239	3
+ 151	10
+ 860	3
+ 441	4
+ 62	5
+ 141	4
+ 381	1
+ 952	5
+ 965	2
+ 315	4
+ 950	2
+ 359	9
+ 351	0
+ 686	7
+ 809	10
+ 397	0
+ 224	5
+ 30	1
+ 859	5
+ 497	9
+ 924	6
+ 331	3
+ 779	3
+ 818	7
+ 474	1
+ 99	4
+ 291	5
+ 316	6
+ 504	0
+ 308	3
+ 970	7
+ 361	2
+ 254	4
+ 277	1
+ 863	8
+ 33	8
+ 413	4
+ 93	2
+ 648	9
+ 937	1
+ 44	0
+ 546	3
+ 493	9
+ 976	10
+ 863	3
+ 310	8
+ 991	7
+ 27	2
+ 63	3
+ 358	9
+ 78	4
+ 714	5
+ 755	8
+ 682	4
+ 717	6
+ 525	8
+ 654	1
+ 97	1
+ 933	1
+ 144	8
+ 358	5
+ 629	3
+ 126	7
+ 593	2
+ 959	10
+ 115	0
+ 342	8
+ 527	1
+ 635	2
+ 501	4
+ 828	0
+ 114	5
+ 96	2
+ 630	0
+ 284	8
+ 825	6
+ 228	5
+ 990	4
+ 109	6
+ 542	1
+ 534	7
+ 105	9
+ 485	6
+ 974	1
+ 842	5
+ 473	7
+ 500	6
+ 152	6
+ 798	8
+ 625	1
+ 555	4
+ 723	8
+ 903	7
+ 136	0
+ 296	7
+ 80	8
+ 334	2
+ 706	8
+ 818	7
+ 940	7
+ 154	4
+ 329	7
+ 1000	5
+ 249	8
+ 264	9
+ 879	8
+ 323	6
+ 602	2
+ 314	7
+ 239	6
+ 416	3
+ 439	7
+ 505	1
+ 569	3
+ 825	5
+ 982	10
+ 921	3
+ 633	9
+ 793	9
+ 718	1
+ 756	6
+ 877	1
+ 198	5
+ 306	5
+ 217	5
+ 121	6
+ 864	6
+ 382	4
+ 706	10
+ 691	5
+ 460	7
+ 511	4
+ 985	1
+ 302	8
+ 26	0
+ 835	8
+ 617	7
+ 862	8
+ 191	2
+ 326	4
+ 713	4
+ 41	6
+ 8	4
+ 946	7
+ 375	6
+ 245	8
+ 310	8
+ 216	3
+ 900	5
+ 73	9
+ 538	9
+ 708	2
+ 620	6
+ 970	8
+ 738	3
+ 219	5
+ 743	3
+ 28	8
+ 683	10
+ 465	1
+ 611	7
+ 893	9
+ 466	1
+ 215	4
+ 626	3
+ 291	2
+ 196	10
+ 319	8
+ 569	3
+ 627	3
+ 585	8
+ 758	3
+ 107	8
+ 80	8
+ 759	5
+ 848	4
+ 255	7
+ 291	7
+ 849	5
+ 87	5
+ 794	4
+ 640	10
+ 378	10
+ 807	9
+ 249	4
+ 253	8
+ 281	0
+ 162	4
+ 797	2
+ 177	6
+ 787	0
+ 926	0
+ 766	2
+ 763	6
+ 723	9
+ 92	5
+ 228	7
+ 507	6
+ 692	3
+ 552	9
+ 748	8
+ 775	0
+ 817	9
+ 417	6
+ 179	6
+ 170	10
+ 619	1
+ 7	4
+ 312	8
+ 1	0
+ 621	1
+ 552	8
+ 825	1
+ 455	5
+ 373	0
+ 457	1
+ 813	2
+ 151	6
+ 169	6
+ 243	3
+ 161	4
+ 314	8
+ 508	3
+ 166	8
+ 92	2
+ 856	7
+ 259	4
+ 561	1
+ 467	0
+ 600	8
+ 24	1
+ 961	8
+ 289	1
+ 467	5
+ 679	7
+ 806	8
+ 124	1
+ 621	6
+ 441	8
+ 453	5
+ 954	3
+ 245	2
+ 716	8
+ 297	2
+ 823	9
+ 22	8
+ 955	10
+ 684	2
+ 95	2
+ 702	8
+ 862	5
+ 615	10
+ 628	2
+ 617	1
+ 23	1
+ 602	10
+ 378	8
+ 189	1
+ 654	5
+ 276	5
+ 383	3
+ 323	3
+ 281	0
+ 582	4
+ 159	3
+ 151	0
+ 793	8
+ 6	4
+ 2	6
+ 491	0
+ 693	1
+ 2	1
+ 941	2
+ 164	6
+ 677	4
+ 71	1
+ 737	4
+ 398	0
+ 402	10
+ 395	6
+ 264	5
+ 581	1
+ 312	6
+ 477	3
+ 209	10
+ 340	9
+ 61	3
+ 972	0
+ 532	1
+ 596	2
+ 576	7
+ 269	3
+ 61	7
+ 331	5
+ 647	7
+ 23	9
+ 272	6
+ 967	6
+ 190	4
+ 899	4
+ 413	2
+ 300	5
+ 581	3
+ 475	1
+ 408	1
+ 323	10
+ 738	6
+ 297	8
+ 259	6
+ 262	9
+ 354	3
+ 817	6
+ 890	8
+ 211	1
+ 230	1
+ 479	6
+ 350	8
+ 116	9
+ 52	6
+ 44	5
+ 662	4
+ 443	4
+ 959	7
+ 200	2
+ 368	5
+ 124	7
+ 748	9
+ 349	6
+ 727	6
+ 718	10
+ 671	2
+ 599	0
+ 977	7
+ 952	0
+ 307	10
+ 488	10
+ 363	9
+ 369	3
+ 672	6
+ 540	0
+ 31	7
+ 763	8
+ 606	1
+ 417	3
+ 672	1
+ 289	3
+ 333	9
+ 364	3
+ 604	3
+ 339	9
+ 311	8
+ 879	7
+ 759	2
+ 996	4
+ 817	5
+ 471	8
+ 199	2
+ 627	8
+ 346	0
+ 138	0
+ 180	4
+ 362	5
+ 316	7
+ 823	9
+ 41	2
+ 830	4
+ 989	7
+ 26	7
+ 957	0
+ 179	8
+ 557	7
+ 622	8
+ 885	2
+ 562	2
+ 294	7
+ 250	5
+ 128	6
+ 987	4
+ 337	8
+ 363	4
+ 972	2
+ 730	10
+ 901	8
+ 710	9
+ 777	9
+ 632	3
+ 540	3
+ 91	4
+ 503	7
+ 656	8
+ 353	9
+ 271	5
+ 517	3
+ 924	9
+ 68	3
+ 232	0
+ 480	10
+ 2	4
+ 717	7
+ 240	5
+ 600	9
+ 829	1
+ 126	9
+ 564	6
+ 573	2
+ 426	9
+ 126	7
+ 406	6
+ 955	3
+ 498	0
+ 618	7
+ 62	1
+ 692	1
+ 481	4
+ 775	7
+ 904	4
+ 592	7
+ 515	7
+ 652	1
+ 347	2
+ 300	8
+ 150	4
+ 471	6
+ 69	4
+ 887	6
+ 448	5
+ 297	5
+ 605	10
+ 574	1
+ 398	3
+ 806	3
+ 725	4
+ 34	2
+ 116	7
+ 320	5
+ 911	6
+ 237	1
+ 46	7
+ 619	1
+ 133	5
+ 682	6
+ 12	10
+ 91	6
+ 967	7
+ 702	4
+ 14	5
+ 667	7
+ 905	7
+ 978	0
+ 387	3
+ 484	3
+ 918	7
+ 361	10
+ 429	10
+ 78	6
+ 485	8
+ 143	5
+ 738	2
+ 113	7
+ 899	8
+ 70	9
+ 322	7
+ 651	2
+ 438	6
+ 247	8
+ 927	7
+ 124	8
+ 453	5
+ 808	9
+ 464	9
+ 445	9
+ 646	6
+ 446	4
+ 822	6
+ 89	7
+ 374	2
+ 633	7
+ 897	3
+ 923	3
+ 913	2
+ 160	8
+ 902	3
+ 684	4
+ 768	5
+ 237	2
+ 378	7
+ 181	0
+ 270	6
+ 408	1
+ 187	5
+ 814	6
+ 657	4
+ 257	6
+ 731	2
+ 889	6
+ 350	0
+ 484	3
+ 333	2
+ 607	1
+ 661	8
+ 333	0
+ 527	5
+ 63	8
+ 142	5
+ 890	3
+ 968	7
+ 889	6
+ 151	1
+ 179	9
+ 325	1
+ 526	7
+ 116	0
+ 927	4
+ 178	5
+ 550	8
+ 379	9
+ 877	9
+ 398	9
+ 703	5
+ 410	6
+ 868	4
+ 297	8
+ 3	4
+ 903	2
+ 329	2
+ 250	9
+ 903	4
+ 865	8
+ 815	0
+ 366	4
+ 881	7
+ 248	8
+ 651	6
+ 698	4
+ 185	1
+ 947	1
+ 487	2
+ 810	5
+ 691	7
+ 672	0
+ 940	9
+ 875	8
+ 287	7
diff --git a/contrib/bloom/expected/bloom.out b/contrib/bloom/expected/bloom.out
new file mode 100644
index ...f7334fa
*** a/contrib/bloom/expected/bloom.out
--- b/contrib/bloom/expected/bloom.out
***************
*** 0 ****
--- 1,120 ----
+ CREATE EXTENSION bloom;
+ CREATE TABLE tst (
+ 	i	int4,
+ 	t	text
+ );
+ \copy tst from 'data/data'
+ CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+ SET enable_seqscan=on;
+ SET enable_bitmapscan=off;
+ SET enable_indexscan=off;
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16;
+                 QUERY PLAN                 
+ -------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: (i = 16)
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: (i = 16)
+ (5 rows)
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE t = '5';
+                 QUERY PLAN                 
+ -------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: (t = '5'::text)
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: (t = '5'::text)
+ (5 rows)
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+                         QUERY PLAN                        
+ ----------------------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: ((i = 16) AND (t = '5'::text))
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: ((i = 16) AND (t = '5'::text))
+ (5 rows)
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ VACUUM ANALYZE tst;
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ VACUUM FULL tst;
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ RESET enable_seqscan;
+ RESET enable_bitmapscan;
+ RESET enable_indexscan;
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
new file mode 100644
index ...e5c7a86
*** a/contrib/bloom/sql/bloom.sql
--- b/contrib/bloom/sql/bloom.sql
***************
*** 0 ****
--- 1,46 ----
+ CREATE EXTENSION bloom;
+ 
+ CREATE TABLE tst (
+ 	i	int4,
+ 	t	text
+ );
+ 
+ \copy tst from 'data/data'
+ 
+ CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+ 
+ SET enable_seqscan=on;
+ SET enable_bitmapscan=off;
+ SET enable_indexscan=off;
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16;
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE t = '5';
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ VACUUM ANALYZE tst;
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ VACUUM FULL tst;
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ RESET enable_seqscan;
+ RESET enable_bitmapscan;
+ RESET enable_indexscan;
diff --git a/contrib/bloom/t/001_wal.pl b/contrib/bloom/t/001_wal.pl
new file mode 100644
index ...77fc161
*** a/contrib/bloom/t/001_wal.pl
--- b/contrib/bloom/t/001_wal.pl
***************
*** 0 ****
--- 1,56 ----
+ use strict;
+ use warnings;
+ use TestLib;
+ use Test::More tests => 31;
+ 
+ use WALTest;
+ 
+ # Run few queries on both master and stanbdy and check their results match.
+ sub test_index_replay
+ {
+ 	my ($test_name) = @_;
+ 
+ 	# Wait until standby apply all changes from master
+ 	WALTest::wait_sync();
+ 
+ 	# Run test queries and compare their result
+ 	check_query(qq(SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ SELECT count(*) FROM tst WHERE i = 0;
+ SELECT count(*) FROM tst WHERE i = 3;
+ SELECT count(*) FROM tst WHERE t = 'b';
+ SELECT count(*) FROM tst WHERE t = 'f';
+ SELECT count(*) FROM tst WHERE i = 3 AND t = 'c';
+ SELECT count(*) FROM tst WHERE i = 7 AND t = 'e';
+ ), $test_name);
+ }
+ 
+ # Make master-standby setup
+ WALTest::setup_cluster();
+ 
+ # Create some bloom index on master
+ master_psql("CREATE EXTENSION bloom;");
+ master_psql("CREATE TABLE tst (i int4, t text);");
+ master_psql("INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,100000) i;");
+ master_psql("CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);");
+ 
+ # Test that queries give same result
+ test_index_replay('initial');
+ 
+ # Run 10 cycles of table modification. Run test queries after each modification.
+ for (my $i = 1; $i <= 10; $i++)
+ {
+ 	master_psql("DELETE FROM tst WHERE i = $i;");
+ 	test_index_replay("delete $i");
+ 	master_psql("VACUUM tst;");
+ 	test_index_replay("vacuum $i");
+ 	my ($start, $end) = (100001 + ($i - 1) * 10000, 100000 + $i * 10000);
+ 	master_psql("INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series($start,$end) i;");
+ 	test_index_replay("insert $i");
+ }
+ 
+ # Cleanup our setup
+ WALTest::clean_cluster();
+ 
+ exit(0);
diff --git a/doc/src/sgml/bloom.sgml b/doc/src/sgml/bloom.sgml
new file mode 100644
index ...c207e6d
*** a/doc/src/sgml/bloom.sgml
--- b/doc/src/sgml/bloom.sgml
***************
*** 0 ****
--- 1,218 ----
+ <!-- doc/src/sgml/bloom.sgml -->
+ 
+ <sect1 id="bloom" xreflabel="bloom">
+  <title>bloom</title>
+ 
+  <indexterm zone="bloom">
+   <primary>bloom</primary>
+  </indexterm>
+ 
+  <para>
+   <literal>bloom</> is a contrib which implements index access method.  It comes
+   as example of custom access methods and generic WAL records usage.  But it
+   is also useful itself.
+  </para>
+ 
+  <sect2>
+   <title>Introduction</title>
+ 
+   <para>
+    Implementation of
+    <ulink url="http://en.wikipedia.org/wiki/Bloom_filter">Bloom filter</ulink>
+    allows fast exclusion of non-candidate tuples.
+    Since signature is a lossy representation of all indexed attributes, 
+    search results should be rechecked using heap information. 
+    User can specify signature length (in uint16, default is 5) and the number of 
+    bits, which can be setted, per attribute (1 < colN < 2048).
+   </para>
+ 
+   <para>
+    This index is useful if table has many attributes and queries can include
+    their arbitary combinations.  Traditional <literal>btree</> index is faster
+    than bloom index, but it'd require too many indexes to support all possible 
+    queries, while one need only one bloom index.  Bloom index supports only 
+    equality comparison.  Since it's a signature file, not a tree, it always
+    should be readed fully, but sequentially, so index search performance is 
+    constant and doesn't depend on a query. 
+   </para>
+  </sect2>
+ 
+  <sect2>
+   <title>Parameters</title>
+ 
+   <para>
+    <literal>bloom</> indexes accept following parameters in <literal>WITH</>
+    clause.
+   </para>
+ 
+    <variablelist>
+    <varlistentry>
+     <term><literal>length</></term>
+     <listitem>
+      <para>
+       Length of signature in uint16 type values
+      </para>
+     </listitem>
+    </varlistentry>
+    </variablelist>
+    <variablelist>
+    <varlistentry>
+     <term><literal>col1 &mdash; col16</></term>
+     <listitem>
+      <para>
+       Number of bits for corresponding column
+      </para>
+     </listitem>
+    </varlistentry>
+    </variablelist>
+  </sect2>
+ 
+  <sect2>
+   <title>Examples</title>
+ 
+   <para>
+    Example of index definition is given below.
+   </para>
+ 
+ <programlisting>
+ CREATE INDEX bloomidx ON tbloom(i1,i2,i3) 
+        WITH (length=5, col1=2, col2=2, col3=4);
+ </programlisting>
+ 
+   <para>
+    Here, we create bloom index with signature length 80 bits and attributes
+    i1, i2  mapped to 2 bits, attribute i3 - to 4 bits.
+   </para>
+ 
+   <para>
+    Example of index definition and usage is given below.
+   </para>
+ 
+ <programlisting>
+ CREATE TABLE tbloom AS
+ SELECT
+     random()::int as i1,
+     random()::int as i2,
+     random()::int as i3,
+     random()::int as i4,
+     random()::int as i5,
+     random()::int as i6,
+     random()::int as i7,
+     random()::int as i8,
+     random()::int as i9,
+     random()::int as i10,
+     random()::int as i11,
+     random()::int as i12,
+     random()::int as i13
+ FROM
+     generate_series(1,1000);
+ CREATE INDEX bloomidx ON tbloom USING
+              bloom (i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12);
+ SELECT pg_relation_size('bloomidx');
+ CREATE index btree_idx ON tbloom(i1,i2,i3,i4,i5,i6,i7,i8,i9,i10,i11,i12);
+ SELECT pg_relation_size('btree_idx');
+ </programlisting>
+ 
+ <programlisting>
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                                    QUERY PLAN
+ -----------------------------------------------------------------------------------------------------------------
+  Bitmap Heap Scan on tbloom  (cost=1.50..5.52 rows=1 width=52) (actual time=0.057..0.057 rows=0 loops=1)
+    Recheck Cond: ((i2 = 20) AND (i10 = 15))
+    ->  Bitmap Index Scan on bloomidx  (cost=0.00..1.50 rows=1 width=0) (actual time=0.041..0.041 rows=9 loops=1)
+          Index Cond: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.081 ms
+ (5 rows)
+ </programlisting>
+ 
+   <para>
+    Seqscan is slow.
+   </para>
+ 
+ <programlisting>
+ =# SET enable_bitmapscan = off;
+ =# SET enable_indexscan = off;
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                             QUERY PLAN
+ --------------------------------------------------------------------------------------------------
+  Seq Scan on tbloom  (cost=0.00..25.00 rows=1 width=52) (actual time=0.162..0.162 rows=0 loops=1)
+    Filter: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.181 ms
+ (3 rows)
+ </programlisting>
+ 
+  <para>
+   Btree index will be not used for this query.
+  </para>
+ 
+ <programlisting>
+ =# DROP INDEX bloomidx;
+ =# CREATE INDEX btree_idx ON tbloom(i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12);
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                             QUERY PLAN
+ --------------------------------------------------------------------------------------------------
+  Seq Scan on tbloom (cost=0.00..25.00 rows=1 width=52) (actual time=0.210..0.210 rows=0 loops=1)
+    Filter: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.250 ms
+ (3 rows)
+ </programlisting>
+  </sect2>
+ 
+  <sect2>
+   <title>Opclass interface</title>
+ 
+   <para>
+    Bloom opclass interface is simple.  It requires 1 supporting function:
+    hash function for indexing datatype.  And it provides 1 search operator:
+    equality operator.  The example below shows <literal>opclass</> definition
+    for <literal>text</> datatype.
+   </para>
+ 
+ <programlisting>
+ CREATE OPERATOR CLASS text_ops
+ DEFAULT FOR TYPE text USING bloom AS
+     OPERATOR    1   =(text, text),
+     FUNCTION    1   hashtext(text);
+ </programlisting>
+  </sect2>
+ 
+  <sect2>
+   <title>Limitation</title>
+   <para>
+ 
+    <itemizedlist>
+     <listitem>
+      <para>
+       For now, only opclasses for <literal>int4</>, <literal>text</> comes
+       with contrib.  However, users may define more of them.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       Only <literal>=</literal> operator is supported for search now.  But it's
+       possible to add support of arrays with contains and intersection
+       operations in future.
+      </para>
+     </listitem>
+    </itemizedlist>
+   </para>
+  </sect2>
+ 
+  <sect2>
+   <title>Authors</title>
+ 
+   <para>
+    Teodor Sigaev <email>teodor@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+ 
+   <para>
+    Alexander Korotkov <email>a.korotkov@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+ 
+   <para>
+    Oleg Bartunov <email>obartunov@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+  </sect2>
+ 
+ </sect1>
diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
new file mode 100644
index 1b3d2d9..c38b2f9
*** a/doc/src/sgml/contrib.sgml
--- b/doc/src/sgml/contrib.sgml
*************** CREATE EXTENSION <replaceable>module_nam
*** 105,110 ****
--- 105,111 ----
   &adminpack;
   &auth-delay;
   &auto-explain;
+  &bloom;
   &btree-gin;
   &btree-gist;
   &chkpass;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
new file mode 100644
index a12fee7..4a93ec2
*** a/doc/src/sgml/filelist.sgml
--- b/doc/src/sgml/filelist.sgml
***************
*** 106,111 ****
--- 106,112 ----
  <!ENTITY adminpack       SYSTEM "adminpack.sgml">
  <!ENTITY auth-delay      SYSTEM "auth-delay.sgml">
  <!ENTITY auto-explain    SYSTEM "auto-explain.sgml">
+ <!ENTITY bloom           SYSTEM "bloom.sgml">
  <!ENTITY btree-gin       SYSTEM "btree-gin.sgml">
  <!ENTITY btree-gist      SYSTEM "btree-gist.sgml">
  <!ENTITY chkpass         SYSTEM "chkpass.sgml">
#49Michael Paquier
michael.paquier@gmail.com
In reply to: Alexander Korotkov (#48)
Re: WIP: Access method extendability

On Fri, Feb 19, 2016 at 12:51 AM, Alexander Korotkov wrote:

11 I'd really like to see regression tests (TAP-tests?) for replication
with generic xlog.

TAP test for replication added to bloom contrib. This test run on additional
make target wal-check.

Just putting my eyes on that...

diff --git a/contrib/bloom/WALTest.pm b/contrib/bloom/WALTest.pm
new file mode 100644
index ...b2daf8b
*** a/contrib/bloom/WALTest.pm
--- b/contrib/bloom/WALTest.pm

This is basically a copy of RewindTest.pm. This is far from generic.
If this patch gets committed first I would suggest to wait for the
more-generic routines that would be added in PostgresNode.pm and then
come back to it.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#50Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Michael Paquier (#49)
3 attachment(s)
Re: WIP: Access method extendability

On Fri, Feb 19, 2016 at 4:08 AM, Michael Paquier <michael.paquier@gmail.com>
wrote:

On Fri, Feb 19, 2016 at 12:51 AM, Alexander Korotkov wrote:

11 I'd really like to see regression tests (TAP-tests?) for replication
with generic xlog.

TAP test for replication added to bloom contrib. This test run on

additional

make target wal-check.

Just putting my eyes on that...

diff --git a/contrib/bloom/WALTest.pm b/contrib/bloom/WALTest.pm
new file mode 100644
index ...b2daf8b
*** a/contrib/bloom/WALTest.pm
--- b/contrib/bloom/WALTest.pm

This is basically a copy of RewindTest.pm. This is far from generic.
If this patch gets committed first I would suggest to wait for the
more-generic routines that would be added in PostgresNode.pm and then
come back to it.

Yes, that's it. Now, with committed changes to PostgresNode.pm, I get rid
of separate WALTest.pm.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

Attachments:

create-am.9.patchapplication/octet-stream; name=create-am.9.patchDownload
diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml
new file mode 100644
index 5f7befb..e5f8f1c
*** a/doc/src/sgml/indexam.sgml
--- b/doc/src/sgml/indexam.sgml
***************
*** 58,63 ****
--- 58,69 ----
    </para>
  
    <para>
+    Index access access methods can be defined and dropped using
+    <xref linkend="sql-createaccessmethod"> and
+     <xref linkend="sql-dropaccessmethod"> SQL commands respectively.
+   </para>
+ 
+   <para>
     An index access method handler function must be declared to accept a
     single argument of type <type>internal</> and to return the
     pseudo-type <type>index_am_handler</>.  The argument is a dummy value that
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
new file mode 100644
index bf95453..77667bd
*** a/doc/src/sgml/ref/allfiles.sgml
--- b/doc/src/sgml/ref/allfiles.sgml
*************** Complete list of usable sgml source file
*** 52,57 ****
--- 52,58 ----
  <!ENTITY commit             SYSTEM "commit.sgml">
  <!ENTITY commitPrepared     SYSTEM "commit_prepared.sgml">
  <!ENTITY copyTable          SYSTEM "copy.sgml">
+ <!ENTITY createAccessMethod SYSTEM "create_access_method.sgml">
  <!ENTITY createAggregate    SYSTEM "create_aggregate.sgml">
  <!ENTITY createCast         SYSTEM "create_cast.sgml">
  <!ENTITY createCollation    SYSTEM "create_collation.sgml">
*************** Complete list of usable sgml source file
*** 94,99 ****
--- 95,101 ----
  <!ENTITY delete             SYSTEM "delete.sgml">
  <!ENTITY discard            SYSTEM "discard.sgml">
  <!ENTITY do                 SYSTEM "do.sgml">
+ <!ENTITY dropAccessMethod   SYSTEM "drop_access_method.sgml">
  <!ENTITY dropAggregate      SYSTEM "drop_aggregate.sgml">
  <!ENTITY dropCast           SYSTEM "drop_cast.sgml">
  <!ENTITY dropCollation      SYSTEM "drop_collation.sgml">
diff --git a/doc/src/sgml/ref/create_access_method.sgml b/doc/src/sgml/ref/create_access_method.sgml
new file mode 100644
index ...f255ecc
*** a/doc/src/sgml/ref/create_access_method.sgml
--- b/doc/src/sgml/ref/create_access_method.sgml
***************
*** 0 ****
--- 1,120 ----
+ <!--
+ doc/src/sgml/ref/create_access_method.sgml
+ PostgreSQL documentation
+ -->
+ 
+ <refentry id="SQL-CREATEACCESSMETHOD">
+  <indexterm zone="sql-createaccessmethod">
+   <primary>CREATE ACCESS METHOD</primary>
+  </indexterm>
+ 
+  <refmeta>
+   <refentrytitle>CREATE ACCESS METHOD</refentrytitle>
+   <manvolnum>7</manvolnum>
+   <refmiscinfo>SQL - Language Statements</refmiscinfo>
+  </refmeta>
+ 
+  <refnamediv>
+   <refname>CREATE ACCESS METHOD</refname>
+   <refpurpose>define a new access method</refpurpose>
+  </refnamediv>
+ 
+  <refsynopsisdiv>
+ <synopsis>
+ CREATE ACCESS METHOD <replaceable class="parameter">name</replaceable>
+     TYPE INDEX
+     HANDLER <replaceable class="parameter">handler_function</replaceable>
+ </synopsis>
+  </refsynopsisdiv>
+ 
+  <refsect1>
+   <title>Description</title>
+ 
+   <para>
+    <command>CREATE ACCESS METHOD</command> creates a new access method.
+   </para>
+ 
+   <para>
+    The access method name must be unique within the database.
+   </para>
+ 
+   <para>
+    Only superusers can define new access methods.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Parameters</title>
+ 
+   <variablelist>
+    <varlistentry>
+     <term><replaceable class="parameter">name</replaceable></term>
+     <listitem>
+      <para>
+       The name of the access method to be created.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>TYPE INDEX</literal></term>
+     <listitem>
+      <para>
+       This clause specifies type of access method to define.
+       For now, there are only index access methods.  But intentionally
+       there would be other types of access methods.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>HANDLER <replaceable class="parameter">handler_function</replaceable></literal></term>
+     <listitem>
+      <para><replaceable class="parameter">handler_function</replaceable> is the
+       name of a previously registered function that will be called to
+       retrieve the struct which contains required parameters and functions
+       of access method to the core.  The handler function must take single
+       argument of type <type>internal</>, and its return type must be
+       <type>index_am_handler</type>.
+      </para>
+ 
+      <para>
+       See <xref linkend="index-api"> for index access methods API.
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Examples</title>
+ 
+   <para>
+    Create an access method <literal>bloom</> with
+    handler function <literal>blhandler</>:
+ <programlisting>
+ CREATE ACCESS METHOD bloom TYPE INDEX HANDLER blhandler;
+ </programlisting>
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Compatibility</title>
+ 
+   <para>
+    <command>CREATE ACCESS METHOD</command> is a
+    <productname>PostgreSQL</> extension.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>See Also</title>
+ 
+   <simplelist type="inline">
+    <member><xref linkend="sql-dropaccessmethod"></member>
+    <member><xref linkend="sql-createopclass"></member>
+    <member><xref linkend="sql-createopfamily"></member>
+   </simplelist>
+  </refsect1>
+ 
+ </refentry>
diff --git a/doc/src/sgml/ref/drop_access_method.sgml b/doc/src/sgml/ref/drop_access_method.sgml
new file mode 100644
index ...354b923
*** a/doc/src/sgml/ref/drop_access_method.sgml
--- b/doc/src/sgml/ref/drop_access_method.sgml
***************
*** 0 ****
--- 1,112 ----
+ <!--
+ doc/src/sgml/ref/drop_access_method.sgml
+ PostgreSQL documentation
+ -->
+ 
+ <refentry id="SQL-DROPACCESSMETHOD">
+  <indexterm zone="sql-dropaccessmethod">
+   <primary>DROP ACCESS METHOD</primary>
+  </indexterm>
+ 
+  <refmeta>
+   <refentrytitle>DROP ACCESS METHOD</refentrytitle>
+   <manvolnum>7</manvolnum>
+   <refmiscinfo>SQL - Language Statements</refmiscinfo>
+  </refmeta>
+ 
+  <refnamediv>
+   <refname>DROP ACCESS METHOD</refname>
+   <refpurpose>remove an access method</refpurpose>
+  </refnamediv>
+ 
+  <refsynopsisdiv>
+ <synopsis>
+ DROP ACCESS METHOD [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ CASCADE | RESTRICT ]
+ </synopsis>
+  </refsynopsisdiv>
+ 
+  <refsect1>
+   <title>Description</title>
+ 
+   <para>
+    <command>DROP ACCESS METHOD</command> removes an existing access method.
+    Only superusers can drop access methods.
+   </para>
+ 
+   <para>
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Parameters</title>
+ 
+   <variablelist>
+    <varlistentry>
+     <term><literal>IF EXISTS</literal></term>
+     <listitem>
+      <para>
+       Do not throw an error if the access method does not exist.
+       A notice is issued in this case.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><replaceable class="parameter">name</replaceable></term>
+     <listitem>
+      <para>
+       The name of an existing access method.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>CASCADE</literal></term>
+     <listitem>
+      <para>
+       Automatically drop objects that depend on the access method
+       (such as operator classes, operator families, indexes).
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>RESTRICT</literal></term>
+     <listitem>
+      <para>
+       Refuse to drop the access method if any objects depend on it.
+       This is the default.
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Examples</title>
+ 
+   <para>
+    Drop the access method <literal>bloom</>:
+ <programlisting>
+ DROP ACCESS METHOD bloom;
+ </programlisting></para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Compatibility</title>
+ 
+   <para>
+    <command>DROP ACCESS METHOD</command> is a
+    <productname>PostgreSQL</> extension.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>See Also</title>
+ 
+   <simplelist type="inline">
+    <member><xref linkend="sql-createaccessmethod"></member>
+   </simplelist>
+  </refsect1>
+ 
+ </refentry>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
new file mode 100644
index 03020df..8acdff1
*** a/doc/src/sgml/reference.sgml
--- b/doc/src/sgml/reference.sgml
***************
*** 80,85 ****
--- 80,86 ----
     &commit;
     &commitPrepared;
     &copyTable;
+    &createAccessMethod;
     &createAggregate;
     &createCast;
     &createCollation;
***************
*** 122,127 ****
--- 123,129 ----
     &delete;
     &discard;
     &do;
+    &dropAccessMethod;
     &dropAggregate;
     &dropCast;
     &dropCollation;
diff --git a/src/backend/access/index/amapi.c b/src/backend/access/index/amapi.c
new file mode 100644
index bda166a..053d3bb
*** a/src/backend/access/index/amapi.c
--- b/src/backend/access/index/amapi.c
*************** GetIndexAmRoutineByAmId(Oid amoid)
*** 62,67 ****
--- 62,74 ----
  			 amoid);
  	amform = (Form_pg_am) GETSTRUCT(tuple);
  
+ 	/* Check if it's index access method */
+ 	if (amform->amtype != 'i')
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("access method \"%s\" type is not index",
+ 						NameStr(amform->amname))));
+ 
  	amhandler = amform->amhandler;
  
  	/* Complain if handler OID is invalid */
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
new file mode 100644
index c48e37b..a86a488
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 20,25 ****
--- 20,26 ----
  #include "catalog/heap.h"
  #include "catalog/index.h"
  #include "catalog/objectaccess.h"
+ #include "catalog/pg_am.h"
  #include "catalog/pg_amop.h"
  #include "catalog/pg_amproc.h"
  #include "catalog/pg_attrdef.h"
*************** static const Oid object_classes[] = {
*** 160,166 ****
  	ExtensionRelationId,		/* OCLASS_EXTENSION */
  	EventTriggerRelationId,		/* OCLASS_EVENT_TRIGGER */
  	PolicyRelationId,			/* OCLASS_POLICY */
! 	TransformRelationId			/* OCLASS_TRANSFORM */
  };
  
  
--- 161,168 ----
  	ExtensionRelationId,		/* OCLASS_EXTENSION */
  	EventTriggerRelationId,		/* OCLASS_EVENT_TRIGGER */
  	PolicyRelationId,			/* OCLASS_POLICY */
! 	TransformRelationId,		/* OCLASS_TRANSFORM */
! 	AccessMethodRelationId		/* OCLASS_AM */
  };
  
  
*************** doDeletion(const ObjectAddress *object, 
*** 1270,1275 ****
--- 1272,1280 ----
  
  		case OCLASS_TRANSFORM:
  			DropTransformById(object->objectId);
+ 
+ 		case OCLASS_AM:
+ 			RemoveAccessMethodById(object->objectId);
  			break;
  
  		default:
*************** getObjectClass(const ObjectAddress *obje
*** 2415,2420 ****
--- 2420,2428 ----
  
  		case TransformRelationId:
  			return OCLASS_TRANSFORM;
+ 
+ 		case AccessMethodRelationId:
+ 			return OCLASS_AM;
  	}
  
  	/* shouldn't get here */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
new file mode 100644
index d2aaa6d..83e8b24
*** a/src/backend/catalog/objectaddress.c
--- b/src/backend/catalog/objectaddress.c
*************** static const ObjectPropertyType ObjectPr
*** 438,443 ****
--- 438,455 ----
  		Anum_pg_type_typacl,
  		ACL_KIND_TYPE,
  		true
+ 	},
+ 	{
+ 		AccessMethodRelationId,
+ 		AmOidIndexId,
+ 		AMOID,
+ 		AMNAME,
+ 		Anum_pg_am_amname,
+ 		InvalidAttrNumber,
+ 		InvalidAttrNumber,
+ 		InvalidAttrNumber,
+ 		-1,
+ 		true
  	}
  };
  
*************** static ObjectAddress get_object_address_
*** 674,679 ****
--- 686,693 ----
  							   List *objargs, bool missing_ok);
  static ObjectAddress get_object_address_defacl(List *objname, List *objargs,
  						  bool missing_ok);
+ static ObjectAddress get_object_address_am(ObjectType objtype, List *objname,
+ 						bool missing_ok);
  static const ObjectPropertyType *get_object_property_data(Oid class_id);
  
  static void getRelationDescription(StringInfo buffer, Oid relid);
*************** get_object_address(ObjectType objtype, L
*** 913,918 ****
--- 927,935 ----
  				address = get_object_address_defacl(objname, objargs,
  													missing_ok);
  				break;
+ 			case OBJECT_ACCESS_METHOD:
+ 				address = get_object_address_am(objtype, objname, missing_ok);
+ 				break;
  			default:
  				elog(ERROR, "unrecognized objtype: %d", (int) objtype);
  				/* placate compiler, in case it thinks elog might return */
*************** get_object_address_opcf(ObjectType objty
*** 1489,1495 ****
  	ObjectAddress address;
  
  	/* XXX no missing_ok support here */
! 	amoid = get_am_oid(strVal(linitial(objname)), false);
  	objname = list_copy_tail(objname, 1);
  
  	switch (objtype)
--- 1506,1512 ----
  	ObjectAddress address;
  
  	/* XXX no missing_ok support here */
! 	amoid = get_am_oid(strVal(linitial(objname)), 'i', false);
  	objname = list_copy_tail(objname, 1);
  
  	switch (objtype)
*************** get_object_address_opcf(ObjectType objty
*** 1516,1521 ****
--- 1533,1597 ----
  }
  
  /*
+  * Find the ObjectAddress for an access method.
+  */
+ static ObjectAddress
+ get_object_address_am(ObjectType objtype, List *objname, bool missing_ok)
+ {
+ 	ObjectAddress	address;
+ 	char		   *amname, *catalogname;
+ 	Type		tup;
+ 
+ 	switch (list_length(objname))
+ 	{
+ 		case 1:
+ 			amname = strVal(linitial(objname));
+ 			break;
+ 		case 2:
+ 			catalogname = strVal(linitial(objname));
+ 			amname = strVal(lsecond(objname));
+ 
+ 			/*
+ 			 * We check the catalog name and then ignore it.
+ 			 */
+ 			if (strcmp(catalogname, get_database_name(MyDatabaseId)) != 0)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				  errmsg("cross-database references are not implemented: %s",
+ 						 NameListToString(objname))));
+ 			break;
+ 		default:
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_SYNTAX_ERROR),
+ 				errmsg("improper access method name (too many dotted names): %s",
+ 					   NameListToString(objname))));
+ 			break;
+ 	}
+ 
+ 	address.classId = AccessMethodRelationId;
+ 	address.objectId = InvalidOid;
+ 	address.objectSubId = 0;
+ 
+ 	tup = SearchSysCache1(AMNAME, PointerGetDatum(amname));
+ 	if (!HeapTupleIsValid(tup))
+ 	{
+ 		/* Access method is missing, report error if needed */
+ 		if (!missing_ok)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 					 errmsg("access method \"%s\" does not exist",
+ 							amname)));
+ 		return address;
+ 	}
+ 
+ 	/* Access method is found, return its oid */
+ 	address.objectId = HeapTupleGetOid(tup);
+ 	ReleaseSysCache(tup);
+ 
+ 	return address;
+ }
+ 
+ /*
   * Find the ObjectAddress for an opclass/opfamily member.
   *
   * (The returned address corresponds to a pg_amop/pg_amproc object).
*************** check_object_ownership(Oid roleid, Objec
*** 2179,2184 ****
--- 2255,2261 ----
  			break;
  		case OBJECT_TSPARSER:
  		case OBJECT_TSTEMPLATE:
+ 		case OBJECT_ACCESS_METHOD:
  			/* We treat these object types as being owned by superusers */
  			if (!superuser_arg(roleid))
  				ereport(ERROR,
*************** getObjectDescription(const ObjectAddress
*** 3129,3134 ****
--- 3206,3226 ----
  				break;
  			}
  
+ 		case OCLASS_AM:
+ 			{
+ 				HeapTuple	tup;
+ 
+ 				tup = SearchSysCache1(AMOID,
+ 									  ObjectIdGetDatum(object->objectId));
+ 				if (!HeapTupleIsValid(tup))
+ 					elog(ERROR, "cache lookup failed for access method %u",
+ 						 object->objectId);
+ 				appendStringInfo(&buffer, _("access method %s"),
+ 							NameStr(((Form_pg_am) GETSTRUCT(tup))->amname));
+ 				ReleaseSysCache(tup);
+ 				break;
+ 			}
+ 
  		default:
  			appendStringInfo(&buffer, "unrecognized object %u %u %d",
  							 object->classId,
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
new file mode 100644
index b1ac704..6b3742c
*** a/src/backend/commands/Makefile
--- b/src/backend/commands/Makefile
*************** subdir = src/backend/commands
*** 12,18 ****
  top_builddir = ../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o  \
  	collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
  	dbcommands.o define.o discard.o dropcmds.o \
  	event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
--- 12,18 ----
  top_builddir = ../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
  	collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
  	dbcommands.o define.o discard.o dropcmds.o \
  	event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
new file mode 100644
index ...6c6996f
*** a/src/backend/commands/amcmds.c
--- b/src/backend/commands/amcmds.c
***************
*** 0 ****
--- 1,277 ----
+ /*-------------------------------------------------------------------------
+  *
+  * amcmds.c
+  *	  Routines for SQL commands that manipulate access methods.
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *
+  * IDENTIFICATION
+  *	  src/backend/commands/amcmds.c
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "access/heapam.h"
+ #include "access/htup_details.h"
+ #include "access/xact.h"
+ #include "catalog/binary_upgrade.h"
+ #include "catalog/catalog.h"
+ #include "catalog/dependency.h"
+ #include "catalog/heap.h"
+ #include "catalog/indexing.h"
+ #include "catalog/objectaccess.h"
+ #include "catalog/pg_authid.h"
+ #include "catalog/pg_am.h"
+ #include "catalog/pg_collation.h"
+ #include "catalog/pg_constraint.h"
+ #include "catalog/pg_depend.h"
+ #include "catalog/pg_enum.h"
+ #include "catalog/pg_language.h"
+ #include "catalog/pg_namespace.h"
+ #include "catalog/pg_proc.h"
+ #include "catalog/pg_proc_fn.h"
+ #include "catalog/pg_range.h"
+ #include "catalog/pg_type.h"
+ #include "catalog/pg_type_fn.h"
+ #include "commands/dbcommands.h"
+ #include "commands/defrem.h"
+ #include "commands/tablecmds.h"
+ #include "commands/typecmds.h"
+ #include "executor/executor.h"
+ #include "miscadmin.h"
+ #include "nodes/makefuncs.h"
+ #include "optimizer/planner.h"
+ #include "optimizer/var.h"
+ #include "parser/parse_coerce.h"
+ #include "parser/parse_collate.h"
+ #include "parser/parse_expr.h"
+ #include "parser/parse_func.h"
+ #include "parser/parse_type.h"
+ #include "utils/acl.h"
+ #include "utils/builtins.h"
+ #include "utils/fmgroids.h"
+ #include "utils/lsyscache.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ #include "utils/snapmgr.h"
+ #include "utils/syscache.h"
+ #include "utils/tqual.h"
+ 
+ /*
+  * Convert a handler function name passed from the parser to an Oid. This
+  * function either return valid function Oid or throw an error.
+  */
+ static Oid
+ lookup_index_am_handler_func(List *handler_name)
+ {
+ 	Oid			handlerOid;
+ 	Oid			funcargtypes[1] = {INTERNALOID};
+ 
+ 	if (handler_name == NIL)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
+ 				 errmsg("handler function is not specified")));
+ 
+ 	/* handlers have no arguments */
+ 	handlerOid = LookupFuncName(handler_name, 1, funcargtypes, false);
+ 
+ 	/* check that handler has correct return type */
+ 	if (get_func_rettype(handlerOid) != INDEX_AM_HANDLEROID)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 				 errmsg("function %s must return type \"index_am_handler\"",
+ 						NameListToString(handler_name))));
+ 
+ 	return handlerOid;
+ }
+ 
+ 
+ /*
+  * CreateAcessMethod
+  *		Registers a new access method.
+  */
+ ObjectAddress
+ CreateAccessMethod(CreateAmStmt *stmt)
+ {
+ 	Relation		rel;
+ 	ObjectAddress	myself;
+ 	ObjectAddress	referenced;
+ 	Oid				amoid;
+ 	Oid				amhandler;
+ 	bool			nulls[Natts_pg_am];
+ 	Datum			values[Natts_pg_am];
+ 	HeapTuple		tup;
+ 
+ 	rel = heap_open(AccessMethodRelationId, RowExclusiveLock);
+ 
+ 	/* Must be super user */
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 			errmsg("permission denied to create access method \"%s\"",
+ 				   stmt->amname),
+ 			errhint("Must be superuser to create access method.")));
+ 
+ 	/* Check if name is busy */
+ 	amoid = GetSysCacheOid1(AMNAME, CStringGetDatum(stmt->amname));
+ 	if (OidIsValid(amoid))
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_DUPLICATE_OBJECT),
+ 				 errmsg("access method \"%s\" already exists", stmt->amname)));
+ 	}
+ 
+ 	/*
+ 	 * Get handler function oid. Handler signature depends on access method
+ 	 * type.
+ 	 */
+ 	switch(stmt->amtype)
+ 	{
+ 		case 'i':
+ 			amhandler = lookup_index_am_handler_func(stmt->handler_name);
+ 			break;
+ 		default:
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 					 errmsg("wrong access method type \"%c\"", stmt->amtype)));
+ 			break;
+ 	}
+ 
+ 	/*
+ 	 * Insert tuple into pg_am.
+ 	 */
+ 	memset(values, 0, sizeof(values));
+ 	memset(nulls, false, sizeof(nulls));
+ 
+ 	values[Anum_pg_am_amname - 1] =
+ 		DirectFunctionCall1(namein, CStringGetDatum(stmt->amname));
+ 	values[Anum_pg_am_amhandler - 1] = ObjectIdGetDatum(amhandler);
+ 	values[Anum_pg_am_amtype - 1] = CharGetDatum(stmt->amtype);
+ 
+ 	tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+ 
+ 	amoid = simple_heap_insert(rel, tup);
+ 	CatalogUpdateIndexes(rel, tup);
+ 	heap_freetuple(tup);
+ 
+ 	myself.classId = AccessMethodRelationId;
+ 	myself.objectId = amoid;
+ 	myself.objectSubId = 0;
+ 
+ 	/* Record dependecy on handler function */
+ 	referenced.classId = ProcedureRelationId;
+ 	referenced.objectId = amhandler;
+ 	referenced.objectSubId = 0;
+ 
+ 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ 
+ 	recordDependencyOnCurrentExtension(&myself, false);
+ 
+ 	heap_close(rel, RowExclusiveLock);
+ 
+ 	return myself;
+ }
+ 
+ /*
+  * Guts of access method deletion.
+  */
+ void
+ RemoveAccessMethodById(Oid amOid)
+ {
+ 	Relation	relation;
+ 	HeapTuple	tup;
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 errmsg("must be superuser to drop access methods")));
+ 
+ 	relation = heap_open(AccessMethodRelationId, RowExclusiveLock);
+ 
+ 	tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
+ 	if (!HeapTupleIsValid(tup))
+ 		elog(ERROR, "cache lookup failed for access method %u", amOid);
+ 
+ 	simple_heap_delete(relation, &tup->t_self);
+ 
+ 	ReleaseSysCache(tup);
+ 
+ 	heap_close(relation, RowExclusiveLock);
+ }
+ 
+ /*
+  * Convert single charater access method type into string for error reporting.
+  */
+ static char *
+ get_am_type_string(char amtype)
+ {
+ 	switch (amtype)
+ 	{
+ 		case 'i':
+ 			return "index";
+ 		default:
+ 			elog(ERROR, "invalid access method type '%c'", amtype);
+ 	}
+ }
+ 
+ /*
+  * get_am_oid - given an access method name and type, look up the OID
+  *
+  * If missing_ok is false, throw an error if access method not found.  If
+  * true, just return InvalidOid.
+  */
+ Oid
+ get_am_oid(const char *amname, char amtype, bool missing_ok)
+ {
+ 	HeapTuple	tup;
+ 	Oid			oid = InvalidOid;
+ 
+ 	tup = SearchSysCache1(AMNAME, CStringGetDatum(amname));
+ 	if (HeapTupleIsValid(tup))
+ 	{
+ 		Form_pg_am amform = (Form_pg_am) GETSTRUCT(tup);
+ 
+ 		if (amform->amtype != amtype)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 					 errmsg("access method \"%s\" type is not %s",
+ 							NameStr(amform->amname),
+ 							get_am_type_string(amtype))));
+ 
+ 		oid = HeapTupleGetOid(tup);
+ 		ReleaseSysCache(tup);
+ 	}
+ 
+ 	if (!OidIsValid(oid) && !missing_ok)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 				 errmsg("access method \"%s\" does not exist", amname)));
+ 	return oid;
+ }
+ 
+ /*
+  * get_am_name - given an access method OID name and type, look up the name
+  *
+  * Access method type is not required for lookup.  However it's useful to check
+  * the type to ensure it is what we're looking for.
+  */
+ char *
+ get_am_name(Oid amOid)
+ {
+ 	HeapTuple	tup;
+ 	char	   *result = NULL;
+ 
+ 	tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
+ 	if (HeapTupleIsValid(tup))
+ 	{
+ 		Form_pg_am amform = (Form_pg_am) GETSTRUCT(tup);
+ 
+ 		result = pstrdup(NameStr(amform->amname));
+ 		ReleaseSysCache(tup);
+ 	}
+ 	return result;
+ }
+ 
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
new file mode 100644
index 9e32f8d..3f52ad8
*** a/src/backend/commands/event_trigger.c
--- b/src/backend/commands/event_trigger.c
*************** typedef enum
*** 86,91 ****
--- 86,92 ----
  
  /* XXX merge this with ObjectTypeMap? */
  static event_trigger_support_data event_trigger_support[] = {
+ 	{"ACCESS METHOD", true},
  	{"AGGREGATE", true},
  	{"CAST", true},
  	{"CONSTRAINT", true},
*************** EventTriggerSupportsObjectType(ObjectTyp
*** 1078,1083 ****
--- 1079,1085 ----
  		case OBJECT_EVENT_TRIGGER:
  			/* no support for event triggers on event triggers */
  			return false;
+ 		case OBJECT_ACCESS_METHOD:
  		case OBJECT_AGGREGATE:
  		case OBJECT_AMOP:
  		case OBJECT_AMPROC:
*************** EventTriggerSupportsObjectClass(ObjectCl
*** 1167,1172 ****
--- 1169,1175 ----
  		case OCLASS_DEFACL:
  		case OCLASS_EXTENSION:
  		case OCLASS_POLICY:
+ 		case OCLASS_AM:
  			return true;
  	}
  
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
new file mode 100644
index 8a66196..d8da8d1
*** a/src/backend/commands/opclasscmds.c
--- b/src/backend/commands/opclasscmds.c
*************** DefineOpClass(CreateOpClassStmt *stmt)
*** 678,683 ****
--- 678,689 ----
  	myself.objectId = opclassoid;
  	myself.objectSubId = 0;
  
+ 	/* dependency on access method */
+ 	referenced.classId = AccessMethodRelationId;
+ 	referenced.objectId = amoid;
+ 	referenced.objectSubId = 0;
+ 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ 
  	/* dependency on namespace */
  	referenced.classId = NamespaceRelationId;
  	referenced.objectId = namespaceoid;
*************** DefineOpFamily(CreateOpFamilyStmt *stmt)
*** 743,749 ****
  					   get_namespace_name(namespaceoid));
  
  	/* Get access method OID, throwing an error if it doesn't exist. */
! 	amoid = get_am_oid(stmt->amname, false);
  
  	/* XXX Should we make any privilege check against the AM? */
  
--- 749,755 ----
  					   get_namespace_name(namespaceoid));
  
  	/* Get access method OID, throwing an error if it doesn't exist. */
! 	amoid = get_am_oid(stmt->amname, 'i', false);
  
  	/* XXX Should we make any privilege check against the AM? */
  
*************** RemoveAmProcEntryById(Oid entryOid)
*** 1663,1683 ****
  	heap_close(rel, RowExclusiveLock);
  }
  
- char *
- get_am_name(Oid amOid)
- {
- 	HeapTuple	tup;
- 	char	   *result = NULL;
- 
- 	tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
- 	if (HeapTupleIsValid(tup))
- 	{
- 		result = pstrdup(NameStr(((Form_pg_am) GETSTRUCT(tup))->amname));
- 		ReleaseSysCache(tup);
- 	}
- 	return result;
- }
- 
  /*
   * Subroutine for ALTER OPERATOR CLASS SET SCHEMA/RENAME
   *
--- 1669,1674 ----
*************** IsThereOpFamilyInNamespace(const char *o
*** 1723,1744 ****
  						get_am_name(opfmethod),
  						get_namespace_name(opfnamespace))));
  }
- 
- /*
-  * get_am_oid - given an access method name, look up the OID
-  *
-  * If missing_ok is false, throw an error if access method not found.  If
-  * true, just return InvalidOid.
-  */
- Oid
- get_am_oid(const char *amname, bool missing_ok)
- {
- 	Oid			oid;
- 
- 	oid = GetSysCacheOid1(AMNAME, CStringGetDatum(amname));
- 	if (!OidIsValid(oid) && !missing_ok)
- 		ereport(ERROR,
- 				(errcode(ERRCODE_UNDEFINED_OBJECT),
- 				 errmsg("access method \"%s\" does not exist", amname)));
- 	return oid;
- }
--- 1714,1716 ----
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
new file mode 100644
index a9e9cc3..85bd5ac
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copyCreateTransformStmt(const CreateTra
*** 3828,3833 ****
--- 3828,3845 ----
  	return newnode;
  }
  
+ static CreateAmStmt *
+ _copyCreateAmStmt(const CreateAmStmt *from)
+ {
+ 	CreateAmStmt *newnode = makeNode(CreateAmStmt);
+ 
+ 	COPY_STRING_FIELD(amname);
+ 	COPY_NODE_FIELD(handler_name);
+ 	COPY_SCALAR_FIELD(amtype);
+ 
+ 	return newnode;
+ }
+ 
  static CreateTrigStmt *
  _copyCreateTrigStmt(const CreateTrigStmt *from)
  {
*************** copyObject(const void *from)
*** 4819,4824 ****
--- 4831,4839 ----
  		case T_CreateTransformStmt:
  			retval = _copyCreateTransformStmt(from);
  			break;
+ 		case T_CreateAmStmt:
+ 			retval = _copyCreateAmStmt(from);
+ 			break;
  		case T_CreateTrigStmt:
  			retval = _copyCreateTrigStmt(from);
  			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
new file mode 100644
index b9c3959..f19fde1
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
*************** _equalCreateTransformStmt(const CreateTr
*** 1855,1860 ****
--- 1855,1870 ----
  }
  
  static bool
+ _equalCreateAmStmt(const CreateAmStmt *a, const CreateAmStmt *b)
+ {
+ 	COMPARE_STRING_FIELD(amname);
+ 	COMPARE_NODE_FIELD(handler_name);
+ 	COMPARE_SCALAR_FIELD(amtype);
+ 
+ 	return true;
+ }
+ 
+ static bool
  _equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b)
  {
  	COMPARE_STRING_FIELD(trigname);
*************** equal(const void *a, const void *b)
*** 3146,3151 ****
--- 3156,3164 ----
  		case T_CreateTransformStmt:
  			retval = _equalCreateTransformStmt(a, b);
  			break;
+ 		case T_CreateAmStmt:
+ 			retval = _equalCreateAmStmt(a, b);
+ 			break;
  		case T_CreateTrigStmt:
  			retval = _equalCreateTrigStmt(a, b);
  			break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
new file mode 100644
index b307b48..fe95580
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
*************** static Node *makeRecursiveViewSelect(cha
*** 263,269 ****
  		DeallocateStmt PrepareStmt ExecuteStmt
  		DropOwnedStmt ReassignOwnedStmt
  		AlterTSConfigurationStmt AlterTSDictionaryStmt
! 		CreateMatViewStmt RefreshMatViewStmt
  
  %type <node>	select_no_parens select_with_parens select_clause
  				simple_select values_clause
--- 263,269 ----
  		DeallocateStmt PrepareStmt ExecuteStmt
  		DropOwnedStmt ReassignOwnedStmt
  		AlterTSConfigurationStmt AlterTSDictionaryStmt
! 		CreateMatViewStmt RefreshMatViewStmt CreateAmStmt DropAmStmt
  
  %type <node>	select_no_parens select_with_parens select_clause
  				simple_select values_clause
*************** static Node *makeRecursiveViewSelect(cha
*** 604,610 ****
  	LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
  	LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
  
! 	MAPPING MATCH MATERIALIZED MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
  
  	NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NONE
  	NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
--- 604,610 ----
  	LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
  	LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
  
! 	MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE
  
  	NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NONE
  	NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
*************** stmt :
*** 789,794 ****
--- 789,795 ----
  			| CommentStmt
  			| ConstraintsSetStmt
  			| CopyStmt
+ 			| CreateAmStmt
  			| CreateAsStmt
  			| CreateAssertStmt
  			| CreateCastStmt
*************** stmt :
*** 823,828 ****
--- 824,830 ----
  			| DeleteStmt
  			| DiscardStmt
  			| DoStmt
+ 			| DropAmStmt
  			| DropAssertStmt
  			| DropCastStmt
  			| DropFdwStmt
*************** row_security_cmd:
*** 4708,4713 ****
--- 4710,4764 ----
  
  /*****************************************************************************
   *
+  *		QUERY:
+  *             CREATE ACCESS METHOD name HANDLER handler_name
+  *
+  *****************************************************************************/
+ 
+ CreateAmStmt: CREATE ACCESS METHOD name TYPE_P INDEX HANDLER handler_name
+ 				{
+ 					CreateAmStmt *n = makeNode(CreateAmStmt);
+ 					n->amname = $4;
+ 					n->handler_name = $8;
+ 					n->amtype = 'i';
+ 					$$ = (Node *) n;
+ 				}
+ 		;
+ 
+ /*****************************************************************************
+  *
+  *		QUERY :
+  *				DROP ACCESS METHOD name
+  *
+  ****************************************************************************/
+ 
+ DropAmStmt: DROP ACCESS METHOD name opt_drop_behavior
+ 				{
+ 					DropStmt *n = makeNode(DropStmt);
+ 					n->removeType = OBJECT_ACCESS_METHOD;
+ 					n->objects = list_make1(list_make1(makeString($4)));
+ 					n->arguments = NIL;
+ 					n->missing_ok = false;
+ 					n->behavior = $5;
+ 					n->concurrent = false;
+ 					$$ = (Node *) n;
+ 				}
+ 				|  DROP ACCESS METHOD IF_P EXISTS name opt_drop_behavior
+ 				{
+ 					DropStmt *n = makeNode(DropStmt);
+ 					n->removeType = OBJECT_ACCESS_METHOD;
+ 					n->objects = list_make1(list_make1(makeString($6)));
+ 					n->arguments = NIL;
+ 					n->missing_ok = true;
+ 					n->behavior = $7;
+ 					n->concurrent = false;
+ 					$$ = (Node *) n;
+ 				}
+ 		;
+ 
+ 
+ /*****************************************************************************
+  *
   *		QUERIES :
   *				CREATE TRIGGER ...
   *				DROP TRIGGER ...
*************** unreserved_keyword:
*** 13778,13783 ****
--- 13829,13835 ----
  			| MATCH
  			| MATERIALIZED
  			| MAXVALUE
+ 			| METHOD
  			| MINUTE_P
  			| MINVALUE
  			| MODE
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
new file mode 100644
index dc431c7..d92864c
*** a/src/backend/parser/parse_utilcmd.c
--- b/src/backend/parser/parse_utilcmd.c
*************** transformIndexConstraint(Constraint *con
*** 1709,1715 ****
  		 * else dump and reload will produce a different index (breaking
  		 * pg_upgrade in particular).
  		 */
! 		if (index_rel->rd_rel->relam != get_am_oid(DEFAULT_INDEX_TYPE, false))
  			ereport(ERROR,
  					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
  					 errmsg("index \"%s\" is not a btree", index_name),
--- 1709,1715 ----
  		 * else dump and reload will produce a different index (breaking
  		 * pg_upgrade in particular).
  		 */
! 		if (index_rel->rd_rel->relam != get_am_oid(DEFAULT_INDEX_TYPE, 'i', false))
  			ereport(ERROR,
  					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
  					 errmsg("index \"%s\" is not a btree", index_name),
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
new file mode 100644
index 045f7f0..4d0aac9
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
*************** ProcessUtilitySlow(Node *parsetree,
*** 1520,1525 ****
--- 1520,1529 ----
  				address = ExecSecLabelStmt((SecLabelStmt *) parsetree);
  				break;
  
+ 			case T_CreateAmStmt:
+ 				address = CreateAccessMethod((CreateAmStmt *) parsetree);
+ 				break;
+ 
  			default:
  				elog(ERROR, "unrecognized node type: %d",
  					 (int) nodeTag(parsetree));
*************** CreateCommandTag(Node *parsetree)
*** 2160,2165 ****
--- 2164,2172 ----
  				case OBJECT_TRANSFORM:
  					tag = "DROP TRANSFORM";
  					break;
+ 				case OBJECT_ACCESS_METHOD:
+ 					tag = "DROP ACCESS METHOD";
+ 					break;
  				default:
  					tag = "???";
  			}
*************** CreateCommandTag(Node *parsetree)
*** 2256,2261 ****
--- 2263,2271 ----
  				case OBJECT_COLLATION:
  					tag = "CREATE COLLATION";
  					break;
+ 				case OBJECT_ACCESS_METHOD:
+ 					tag = "CREATE ACCESS METHOD";
+ 					break;
  				default:
  					tag = "???";
  			}
*************** CreateCommandTag(Node *parsetree)
*** 2519,2524 ****
--- 2529,2538 ----
  			tag = "ALTER POLICY";
  			break;
  
+ 		case T_CreateAmStmt:
+ 			tag = "CREATE ACCESS METHOD";
+ 			break;
+ 
  		case T_PrepareStmt:
  			tag = "PREPARE";
  			break;
*************** GetCommandLogLevel(Node *parsetree)
*** 3076,3081 ****
--- 3090,3099 ----
  			lev = LOGSTMT_DDL;
  			break;
  
+ 		case T_CreateAmStmt:
+ 			lev = LOGSTMT_DDL;
+ 			break;
+ 
  			/* already-planned queries */
  		case T_PlannedStmt:
  			{
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
new file mode 100644
index 46c95b0..516af92
*** a/src/backend/utils/adt/selfuncs.c
--- b/src/backend/utils/adt/selfuncs.c
*************** string_to_bytea_const(const char *str, s
*** 6012,6032 ****
   *-------------------------------------------------------------------------
   */
  
! /*
!  * deconstruct_indexquals is a simple function to examine the indexquals
!  * attached to a proposed IndexPath.  It returns a list of IndexQualInfo
!  * structs, one per qual expression.
!  */
! typedef struct
! {
! 	RestrictInfo *rinfo;		/* the indexqual itself */
! 	int			indexcol;		/* zero-based index column number */
! 	bool		varonleft;		/* true if index column is on left of qual */
! 	Oid			clause_op;		/* qual's operator OID, if relevant */
! 	Node	   *other_operand;	/* non-index operand of qual's operator */
! } IndexQualInfo;
! 
! static List *
  deconstruct_indexquals(IndexPath *path)
  {
  	List	   *result = NIL;
--- 6012,6018 ----
   *-------------------------------------------------------------------------
   */
  
! List *
  deconstruct_indexquals(IndexPath *path)
  {
  	List	   *result = NIL;
*************** orderby_operands_eval_cost(PlannerInfo *
*** 6176,6210 ****
  	return qual_arg_cost;
  }
  
! /*
!  * genericcostestimate is a general-purpose estimator that can be used for
!  * most index types.  In some cases we use genericcostestimate as the base
!  * code and then incorporate additional index-type-specific knowledge in
!  * the type-specific calling function.  To avoid code duplication, we make
!  * genericcostestimate return a number of intermediate values as well as
!  * its preliminary estimates of the output cost values.  The GenericCosts
!  * struct includes all these values.
!  *
!  * Callers should initialize all fields of GenericCosts to zero.  In addition,
!  * they can set numIndexTuples to some positive value if they have a better
!  * than default way of estimating the number of leaf index tuples visited.
!  */
! typedef struct
! {
! 	/* These are the values the cost estimator must return to the planner */
! 	Cost		indexStartupCost;		/* index-related startup cost */
! 	Cost		indexTotalCost; /* total index-related scan cost */
! 	Selectivity indexSelectivity;		/* selectivity of index */
! 	double		indexCorrelation;		/* order correlation of index */
! 
! 	/* Intermediate values we obtain along the way */
! 	double		numIndexPages;	/* number of leaf pages visited */
! 	double		numIndexTuples; /* number of leaf tuples visited */
! 	double		spc_random_page_cost;	/* relevant random_page_cost value */
! 	double		num_sa_scans;	/* # indexscans from ScalarArrayOps */
! } GenericCosts;
! 
! static void
  genericcostestimate(PlannerInfo *root,
  					IndexPath *path,
  					double loop_count,
--- 6162,6168 ----
  	return qual_arg_cost;
  }
  
! void
  genericcostestimate(PlannerInfo *root,
  					IndexPath *path,
  					double loop_count,
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
new file mode 100644
index f798b15..1acd91a
*** a/src/bin/pg_dump/common.c
--- b/src/bin/pg_dump/common.c
*************** getSchemaData(Archive *fout, int *numTab
*** 98,103 ****
--- 98,104 ----
  	int			numProcLangs;
  	int			numCasts;
  	int			numTransforms;
+ 	int			numAccessMethods;
  	int			numOpclasses;
  	int			numOpfamilies;
  	int			numConversions;
*************** getSchemaData(Archive *fout, int *numTab
*** 169,174 ****
--- 170,179 ----
  	oprinfoindex = buildIndexArray(oprinfo, numOperators, sizeof(OprInfo));
  
  	if (g_verbose)
+ 		write_msg(NULL, "reading user-defined access methods\n");
+ 	getAccessMethods(fout, &numAccessMethods);
+ 
+ 	if (g_verbose)
  		write_msg(NULL, "reading user-defined operator classes\n");
  	getOpclasses(fout, &numOpclasses);
  
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
new file mode 100644
index 64c2673..8999bcc
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
*************** static void dumpFunc(Archive *fout, Func
*** 173,178 ****
--- 173,179 ----
  static void dumpCast(Archive *fout, CastInfo *cast);
  static void dumpTransform(Archive *fout, TransformInfo *transform);
  static void dumpOpr(Archive *fout, OprInfo *oprinfo);
+ static void dumpAccessMethod(Archive *fout, AccessMethodInfo *oprinfo);
  static void dumpOpclass(Archive *fout, OpclassInfo *opcinfo);
  static void dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo);
  static void dumpCollation(Archive *fout, CollInfo *convinfo);
*************** getConversions(Archive *fout, int *numCo
*** 4101,4106 ****
--- 4102,4183 ----
  }
  
  /*
+  * getAccessMethods:
+  *	  read all user-defined access methods in the system catalogs and return
+  *    them in the AccessMethodInfo* structure
+  *
+  *	numAccessMethods is set to the number of access methods read in
+  */
+ AccessMethodInfo *
+ getAccessMethods(Archive *fout, int *numAccessMethods)
+ {
+ 	DumpOptions *dopt = fout->dopt;
+ 	PGresult   *res;
+ 	int			ntups;
+ 	int			i;
+ 	PQExpBuffer query;
+ 	AccessMethodInfo *aminfo;
+ 	int			i_tableoid;
+ 	int			i_oid;
+ 	int			i_amname;
+ 	int			i_amhandler;
+ 
+ 	/* Before 9.6, there are no user-defined access methods */
+ 	if (fout->remoteVersion < 90600)
+ 	{
+ 		*numAccessMethods = 0;
+ 		return NULL;
+ 	}
+ 
+ 	query = createPQExpBuffer();
+ 
+ 	/* Make sure we are in proper schema */
+ 	selectSourceSchema(fout, "pg_catalog");
+ 
+ 	/*
+ 	 * Select only user-defined access methods assuming all built-in access
+ 	 * methods have oid < 10000.
+ 	 */
+ 	appendPQExpBuffer(query, "SELECT tableoid, oid, amname, "
+ 					  "amhandler::pg_catalog.regproc AS amhandler "
+ 					  "FROM pg_am "
+ 					  "WHERE oid >= 10000");
+ 
+ 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+ 
+ 	ntups = PQntuples(res);
+ 	*numAccessMethods = ntups;
+ 
+ 	aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
+ 
+ 	i_tableoid = PQfnumber(res, "tableoid");
+ 	i_oid = PQfnumber(res, "oid");
+ 	i_amname = PQfnumber(res, "amname");
+ 	i_amhandler = PQfnumber(res, "amhandler");
+ 
+ 	for (i = 0; i < ntups; i++)
+ 	{
+ 		aminfo[i].dobj.objType = DO_ACCESS_METHOD;
+ 		aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ 		aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ 		AssignDumpId(&aminfo[i].dobj);
+ 		aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
+ 		aminfo[i].dobj.namespace = NULL;
+ 		aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
+ 
+ 		/* Decide whether we want to dump it */
+ 		selectDumpableObject(&(aminfo[i].dobj), dopt);
+ 	}
+ 
+ 	PQclear(res);
+ 
+ 	destroyPQExpBuffer(query);
+ 
+ 	return aminfo;
+ }
+ 
+ 
+ /*
   * getOpclasses:
   *	  read all opclasses in the system catalogs and return them in the
   * OpclassInfo* structure
*************** dumpDumpableObject(Archive *fout, Dumpab
*** 8408,8413 ****
--- 8485,8493 ----
  		case DO_OPERATOR:
  			dumpOpr(fout, (OprInfo *) dobj);
  			break;
+ 		case DO_ACCESS_METHOD:
+ 			dumpAccessMethod(fout, (AccessMethodInfo *) dobj);
+ 			break;
  		case DO_OPCLASS:
  			dumpOpclass(fout, (OpclassInfo *) dobj);
  			break;
*************** convertTSFunction(Archive *fout, Oid fun
*** 11446,11451 ****
--- 11526,11584 ----
  	return result;
  }
  
+ /*
+  * dumpAccessMethod
+  *	  write out a single access method definition
+  */
+ static void
+ dumpAccessMethod(Archive *fout, AccessMethodInfo *aminfo)
+ {
+ 	DumpOptions *dopt = fout->dopt;
+ 	PQExpBuffer q;
+ 	PQExpBuffer delq;
+ 	PQExpBuffer labelq;
+ 	char	   *qamname;
+ 
+ 	/* Skip if not to be dumped */
+ 	if (!aminfo->dobj.dump || dopt->dataOnly)
+ 		return;
+ 
+ 	q = createPQExpBuffer();
+ 	delq = createPQExpBuffer();
+ 	labelq = createPQExpBuffer();
+ 
+ 	qamname = pg_strdup(fmtId(aminfo->dobj.name));
+ 
+ 	appendPQExpBuffer(q, "CREATE ACCESS METHOD %s HANDLER %s;\n",
+ 					  qamname, aminfo->amhandler);
+ 
+ 	appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
+ 					  qamname);
+ 
+ 	appendPQExpBuffer(labelq, "ACCESS METHOD %s",
+ 					  qamname);
+ 
+ 	ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
+ 				 aminfo->dobj.name,
+ 				 NULL,
+ 				 NULL,
+ 				 "",
+ 				 false, "ACCESS METHOD", SECTION_PRE_DATA,
+ 				 q->data, delq->data, NULL,
+ 				 NULL, 0,
+ 				 NULL, NULL);
+ 
+ 	/* Dump Access Method Comments */
+ 	dumpComment(fout, labelq->data,
+ 				NULL, "",
+ 				aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
+ 
+ 	free(qamname);
+ 
+ 	destroyPQExpBuffer(q);
+ 	destroyPQExpBuffer(delq);
+ 	destroyPQExpBuffer(labelq);
+ }
  
  /*
   * dumpOpclass
*************** addBoundaryDependencies(DumpableObject *
*** 16227,16232 ****
--- 16360,16366 ----
  			case DO_FUNC:
  			case DO_AGG:
  			case DO_OPERATOR:
+ 			case DO_ACCESS_METHOD:
  			case DO_OPCLASS:
  			case DO_OPFAMILY:
  			case DO_COLLATION:
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
new file mode 100644
index 9a1d8f8..0fb6087
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
*************** typedef enum
*** 48,53 ****
--- 48,54 ----
  	DO_FUNC,
  	DO_AGG,
  	DO_OPERATOR,
+ 	DO_ACCESS_METHOD,
  	DO_OPCLASS,
  	DO_OPFAMILY,
  	DO_COLLATION,
*************** typedef struct _oprInfo
*** 167,172 ****
--- 168,179 ----
  	Oid			oprcode;
  } OprInfo;
  
+ typedef struct _accessMethodInfo
+ {
+ 	DumpableObject dobj;
+ 	char	   *amhandler;
+ } AccessMethodInfo;
+ 
  typedef struct _opclassInfo
  {
  	DumpableObject dobj;
*************** extern TypeInfo *getTypes(Archive *fout,
*** 548,553 ****
--- 555,561 ----
  extern FuncInfo *getFuncs(Archive *fout, int *numFuncs);
  extern AggInfo *getAggregates(Archive *fout, int *numAggregates);
  extern OprInfo *getOperators(Archive *fout, int *numOperators);
+ extern AccessMethodInfo *getAccessMethods(Archive *fout, int *numAccessMethods);
  extern OpclassInfo *getOpclasses(Archive *fout, int *numOpclasses);
  extern OpfamilyInfo *getOpfamilies(Archive *fout, int *numOpfamilies);
  extern CollInfo *getCollations(Archive *fout, int *numCollations);
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
new file mode 100644
index 78ff59c..3bd5bd8
*** a/src/bin/pg_dump/pg_dump_sort.c
--- b/src/bin/pg_dump/pg_dump_sort.c
*************** static const int oldObjectTypePriority[]
*** 45,50 ****
--- 45,51 ----
  	2,							/* DO_FUNC */
  	3,							/* DO_AGG */
  	3,							/* DO_OPERATOR */
+ 	3,							/* DO_ACCESS_METHOD */
  	4,							/* DO_OPCLASS */
  	4,							/* DO_OPFAMILY */
  	4,							/* DO_COLLATION */
*************** static const int newObjectTypePriority[]
*** 95,100 ****
--- 96,102 ----
  	6,							/* DO_FUNC */
  	7,							/* DO_AGG */
  	8,							/* DO_OPERATOR */
+ 	8,							/* DO_ACCESS_METHOD */
  	9,							/* DO_OPCLASS */
  	9,							/* DO_OPFAMILY */
  	3,							/* DO_COLLATION */
*************** describeDumpableObject(DumpableObject *o
*** 1329,1334 ****
--- 1331,1340 ----
  					 "OPERATOR %s  (ID %d OID %u)",
  					 obj->name, obj->dumpId, obj->catId.oid);
  			return;
+ 		case DO_ACCESS_METHOD:
+ 			snprintf(buf, bufsize,
+ 					 "");
+ 			return;
  		case DO_OPCLASS:
  			snprintf(buf, bufsize,
  					 "OPERATOR CLASS %s  (ID %d OID %u)",
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
new file mode 100644
index 049bf9f..ac16740
*** a/src/include/catalog/dependency.h
--- b/src/include/catalog/dependency.h
*************** typedef enum ObjectClass
*** 153,162 ****
  	OCLASS_EXTENSION,			/* pg_extension */
  	OCLASS_EVENT_TRIGGER,		/* pg_event_trigger */
  	OCLASS_POLICY,				/* pg_policy */
! 	OCLASS_TRANSFORM			/* pg_transform */
  } ObjectClass;
  
! #define LAST_OCLASS		OCLASS_TRANSFORM
  
  
  /* in dependency.c */
--- 153,163 ----
  	OCLASS_EXTENSION,			/* pg_extension */
  	OCLASS_EVENT_TRIGGER,		/* pg_event_trigger */
  	OCLASS_POLICY,				/* pg_policy */
! 	OCLASS_TRANSFORM,			/* pg_transform */
! 	OCLASS_AM,					/* pg_am */
  } ObjectClass;
  
! #define LAST_OCLASS		OCLASS_AM
  
  
  /* in dependency.c */
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
new file mode 100644
index f801c3e..605e154
*** a/src/include/catalog/pg_am.h
--- b/src/include/catalog/pg_am.h
*************** CATALOG(pg_am,2601)
*** 35,40 ****
--- 35,47 ----
  {
  	NameData	amname;			/* access method name */
  	regproc		amhandler;		/* handler function */
+ 
+ 	/*----------
+ 	 * Type of access method. Possible values are
+ 	 *		'i': Index access method
+ 	 *----------
+ 	 */
+ 	char		amtype;
  } FormData_pg_am;
  
  /* ----------------
*************** typedef FormData_pg_am *Form_pg_am;
*** 48,78 ****
   *		compiler constants for pg_am
   * ----------------
   */
! #define Natts_pg_am						2
  #define Anum_pg_am_amname				1
  #define Anum_pg_am_amhandler			2
  
  /* ----------------
   *		initial contents of pg_am
   * ----------------
   */
  
! DATA(insert OID = 403 (  btree		bthandler ));
  DESCR("b-tree index access method");
  #define BTREE_AM_OID 403
! DATA(insert OID = 405 (  hash		hashhandler ));
  DESCR("hash index access method");
  #define HASH_AM_OID 405
! DATA(insert OID = 783 (  gist		gisthandler ));
  DESCR("GiST index access method");
  #define GIST_AM_OID 783
! DATA(insert OID = 2742 (  gin		ginhandler ));
  DESCR("GIN index access method");
  #define GIN_AM_OID 2742
! DATA(insert OID = 4000 (  spgist	spghandler ));
  DESCR("SP-GiST index access method");
  #define SPGIST_AM_OID 4000
! DATA(insert OID = 3580 (  brin		brinhandler ));
  DESCR("block range index (BRIN) access method");
  #define BRIN_AM_OID 3580
  
--- 55,86 ----
   *		compiler constants for pg_am
   * ----------------
   */
! #define Natts_pg_am						3
  #define Anum_pg_am_amname				1
  #define Anum_pg_am_amhandler			2
+ #define Anum_pg_am_amtype				3
  
  /* ----------------
   *		initial contents of pg_am
   * ----------------
   */
  
! DATA(insert OID = 403 (  btree		bthandler	i ));
  DESCR("b-tree index access method");
  #define BTREE_AM_OID 403
! DATA(insert OID = 405 (  hash		hashhandler	i ));
  DESCR("hash index access method");
  #define HASH_AM_OID 405
! DATA(insert OID = 783 (  gist		gisthandler	i ));
  DESCR("GiST index access method");
  #define GIST_AM_OID 783
! DATA(insert OID = 2742 (  gin		ginhandler	i ));
  DESCR("GIN index access method");
  #define GIN_AM_OID 2742
! DATA(insert OID = 4000 (  spgist	spghandler	i ));
  DESCR("SP-GiST index access method");
  #define SPGIST_AM_OID 4000
! DATA(insert OID = 3580 (  brin		brinhandler	i ));
  DESCR("block range index (BRIN) access method");
  #define BRIN_AM_OID 3580
  
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
new file mode 100644
index 54f67e9..1e21f03
*** a/src/include/commands/defrem.h
--- b/src/include/commands/defrem.h
*************** extern void IsThereOpClassInNamespace(co
*** 91,98 ****
  						  Oid opcnamespace);
  extern void IsThereOpFamilyInNamespace(const char *opfname, Oid opfmethod,
  						   Oid opfnamespace);
- extern Oid	get_am_oid(const char *amname, bool missing_ok);
- extern char *get_am_name(Oid amOid);
  extern Oid	get_opclass_oid(Oid amID, List *opclassname, bool missing_ok);
  extern Oid	get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok);
  
--- 91,96 ----
*************** extern Datum transformGenericOptions(Oid
*** 137,142 ****
--- 135,146 ----
  						List *options,
  						Oid fdwvalidator);
  
+ /* commands/amcmds.c */
+ extern ObjectAddress CreateAccessMethod(CreateAmStmt *stmt);
+ extern void RemoveAccessMethodById(Oid amOid);
+ extern Oid	get_am_oid(const char *amname, char amtype, bool missing_ok);
+ extern char *get_am_name(Oid amOid);
+ 
  /* support routines in commands/define.c */
  
  extern char *defGetString(DefElem *def);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
new file mode 100644
index c407fa2..7d46763
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
*************** typedef enum NodeTag
*** 386,391 ****
--- 386,392 ----
  	T_CreatePolicyStmt,
  	T_AlterPolicyStmt,
  	T_CreateTransformStmt,
+ 	T_CreateAmStmt,
  
  	/*
  	 * TAGS FOR PARSE TREE NODES (parsenodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
new file mode 100644
index 2fd0629..8b958b4
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
*************** typedef struct SetOperationStmt
*** 1379,1384 ****
--- 1379,1385 ----
  
  typedef enum ObjectType
  {
+ 	OBJECT_ACCESS_METHOD,
  	OBJECT_AGGREGATE,
  	OBJECT_AMOP,
  	OBJECT_AMPROC,
*************** typedef struct AlterPolicyStmt
*** 2070,2075 ****
--- 2071,2088 ----
  	Node	   *with_check;		/* the policy's WITH CHECK condition. */
  } AlterPolicyStmt;
  
+ /*----------------------
+  *		Create ACCESS METHOD Statement
+  *----------------------
+  */
+ typedef struct CreateAmStmt
+ {
+ 	NodeTag		type;
+ 	char	   *amname;			/* access method name */
+ 	List	   *handler_name;	/* handler function name */
+ 	char		amtype;			/* type of access method */
+ } CreateAmStmt;
+ 
  /* ----------------------
   *		Create TRIGGER Statement
   * ----------------------
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
new file mode 100644
index 6e1e820..7de3404
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
*************** PG_KEYWORD("mapping", MAPPING, UNRESERVE
*** 239,244 ****
--- 239,245 ----
  PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD)
  PG_KEYWORD("materialized", MATERIALIZED, UNRESERVED_KEYWORD)
  PG_KEYWORD("maxvalue", MAXVALUE, UNRESERVED_KEYWORD)
+ PG_KEYWORD("method", METHOD, UNRESERVED_KEYWORD)
  PG_KEYWORD("minute", MINUTE_P, UNRESERVED_KEYWORD)
  PG_KEYWORD("minvalue", MINVALUE, UNRESERVED_KEYWORD)
  PG_KEYWORD("mode", MODE, UNRESERVED_KEYWORD)
diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h
new file mode 100644
index 06fbca7..7fb7466
*** a/src/include/utils/selfuncs.h
--- b/src/include/utils/selfuncs.h
*************** typedef enum
*** 95,100 ****
--- 95,142 ----
  	Pattern_Prefix_None, Pattern_Prefix_Partial, Pattern_Prefix_Exact
  } Pattern_Prefix_Status;
  
+ /*
+  * deconstruct_indexquals is a simple function to examine the indexquals
+  * attached to a proposed IndexPath.  It returns a list of IndexQualInfo
+  * structs, one per qual expression.
+  */
+ typedef struct
+ {
+ 	RestrictInfo *rinfo;		/* the indexqual itself */
+ 	int			indexcol;		/* zero-based index column number */
+ 	bool		varonleft;		/* true if index column is on left of qual */
+ 	Oid			clause_op;		/* qual's operator OID, if relevant */
+ 	Node	   *other_operand;	/* non-index operand of qual's operator */
+ } IndexQualInfo;
+ 
+ /*
+  * genericcostestimate is a general-purpose estimator that can be used for
+  * most index types.  In some cases we use genericcostestimate as the base
+  * code and then incorporate additional index-type-specific knowledge in
+  * the type-specific calling function.  To avoid code duplication, we make
+  * genericcostestimate return a number of intermediate values as well as
+  * its preliminary estimates of the output cost values.  The GenericCosts
+  * struct includes all these values.
+  *
+  * Callers should initialize all fields of GenericCosts to zero.  In addition,
+  * they can set numIndexTuples to some positive value if they have a better
+  * than default way of estimating the number of leaf index tuples visited.
+  */
+ typedef struct
+ {
+ 	/* These are the values the cost estimator must return to the planner */
+ 	Cost		indexStartupCost;		/* index-related startup cost */
+ 	Cost		indexTotalCost; /* total index-related scan cost */
+ 	Selectivity indexSelectivity;		/* selectivity of index */
+ 	double		indexCorrelation;		/* order correlation of index */
+ 
+ 	/* Intermediate values we obtain along the way */
+ 	double		numIndexPages;	/* number of leaf pages visited */
+ 	double		numIndexTuples; /* number of leaf tuples visited */
+ 	double		spc_random_page_cost;	/* relevant random_page_cost value */
+ 	double		num_sa_scans;	/* # indexscans from ScalarArrayOps */
+ } GenericCosts;
+ 
  /* Hooks for plugins to get control when we ask for stats */
  typedef bool (*get_relation_stats_hook_type) (PlannerInfo *root,
  														  RangeTblEntry *rte,
*************** extern double estimate_num_groups(Planne
*** 191,196 ****
--- 233,244 ----
  extern Selectivity estimate_hash_bucketsize(PlannerInfo *root, Node *hashkey,
  						 double nbuckets);
  
+ extern List *deconstruct_indexquals(IndexPath *path);
+ extern void genericcostestimate(PlannerInfo *root, IndexPath *path,
+ 								double loop_count,
+ 								List *qinfos,
+ 								GenericCosts *costs);
+ 
  /* Functions in array_selfuncs.c */
  
  extern Selectivity scalararraysel_containment(PlannerInfo *root,
diff --git a/src/test/regress/expected/create_am.out b/src/test/regress/expected/create_am.out
new file mode 100644
index ...47d6024
*** a/src/test/regress/expected/create_am.out
--- b/src/test/regress/expected/create_am.out
***************
*** 0 ****
--- 1,108 ----
+ --
+ -- Create access method tests
+ --
+ -- Make gist2 over gisthandler. In fact, it would be a synonym to gist.
+ CREATE ACCESS METHOD gist2 TYPE INDEX HANDLER gisthandler;
+ -- Drop old index on fast_emp4000
+ DROP INDEX grect2ind;
+ -- Try to create gist2 index on fast_emp4000: fail because opclass doesn't exist
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ ERROR:  data type box has no default operator class for access method "gist2"
+ HINT:  You must specify an operator class for the index or define a default operator class for the data type.
+ -- Make operator class for boxes using gist2
+ CREATE OPERATOR CLASS box_ops DEFAULT
+ 	FOR TYPE box USING gist2 AS
+ 	OPERATOR 1	<<,
+ 	OPERATOR 2	&<,
+ 	OPERATOR 3	&&,
+ 	OPERATOR 4	&>,
+ 	OPERATOR 5	>>,
+ 	OPERATOR 6	~=,
+ 	OPERATOR 7	@>,
+ 	OPERATOR 8	<@,
+ 	OPERATOR 9	&<|,
+ 	OPERATOR 10	<<|,
+ 	OPERATOR 11	|>>,
+ 	OPERATOR 12	|&>,
+ 	OPERATOR 13	~,
+ 	OPERATOR 14	@,
+ 	FUNCTION 1	gist_box_consistent(internal, box, smallint, oid, internal),
+ 	FUNCTION 2	gist_box_union(internal, internal),
+ 	FUNCTION 3	gist_box_compress(internal),
+ 	FUNCTION 4	gist_box_decompress(internal),
+ 	FUNCTION 5	gist_box_penalty(internal, internal, internal),
+ 	FUNCTION 6	gist_box_picksplit(internal, internal),
+ 	FUNCTION 7	gist_box_same(box, box, internal),
+ 	FUNCTION 9	gist_box_fetch(internal);
+ -- Create gist2 index on fast_emp4000
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ -- Now check the results from plain indexscan
+ SET enable_seqscan = OFF;
+ SET enable_indexscan = ON;
+ SET enable_bitmapscan = OFF;
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+                            QUERY PLAN                           
+ ----------------------------------------------------------------
+  Sort
+    Sort Key: ((home_base[0])[0])
+    ->  Index Only Scan using grect2ind on fast_emp4000
+          Index Cond: (home_base @ '(2000,1000),(200,200)'::box)
+ (4 rows)
+ 
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+        home_base       
+ -----------------------
+  (337,455),(240,359)
+  (1444,403),(1346,344)
+ (2 rows)
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+                          QUERY PLAN                          
+ -------------------------------------------------------------
+  Aggregate
+    ->  Index Only Scan using grect2ind on fast_emp4000
+          Index Cond: (home_base && '(1000,1000),(0,0)'::box)
+ (3 rows)
+ 
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+  count 
+ -------
+      2
+ (1 row)
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+                       QUERY PLAN                       
+ -------------------------------------------------------
+  Aggregate
+    ->  Index Only Scan using grect2ind on fast_emp4000
+          Index Cond: (home_base IS NULL)
+ (3 rows)
+ 
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+  count 
+ -------
+    278
+ (1 row)
+ 
+ -- Try to drop access method: fail because of depending objects
+ DROP ACCESS METHOD gist2;
+ ERROR:  cannot drop access method gist2 because other objects depend on it
+ DETAIL:  operator class box_ops for access method gist2 depends on access method gist2
+ index grect2ind depends on operator class box_ops for access method gist2
+ HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+ -- Drop access method cascade
+ DROP ACCESS METHOD gist2 CASCADE;
+ NOTICE:  drop cascades to 2 other objects
+ DETAIL:  drop cascades to operator class box_ops for access method gist2
+ drop cascades to index grect2ind
+ -- Reset optimizer options
+ RESET enable_seqscan;
+ RESET enable_indexscan;
+ RESET enable_bitmapscan;
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
new file mode 100644
index eb0bc88..2c5be4b
*** a/src/test/regress/expected/sanity_check.out
--- b/src/test/regress/expected/sanity_check.out
*************** e_star|f
*** 44,50 ****
  emp|f
  equipment_r|f
  f_star|f
! fast_emp4000|t
  float4_tbl|f
  float8_tbl|f
  func_index_heap|t
--- 44,50 ----
  emp|f
  equipment_r|f
  f_star|f
! fast_emp4000|f
  float4_tbl|f
  float8_tbl|f
  func_index_heap|t
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
new file mode 100644
index bec0316..8be4b83
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
*************** test: create_index create_view
*** 60,66 ****
  # ----------
  # Another group of parallel tests
  # ----------
! test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes
  
  # ----------
  # sanity_check does a vacuum, affecting the sort order of SELECT *
--- 60,66 ----
  # ----------
  # Another group of parallel tests
  # ----------
! test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes create_am
  
  # ----------
  # sanity_check does a vacuum, affecting the sort order of SELECT *
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
new file mode 100644
index 7e9b319..1de3da8
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
*************** test: drop_if_exists
*** 75,80 ****
--- 75,81 ----
  test: updatable_views
  test: rolenames
  test: roleattributes
+ test: create_am
  test: sanity_check
  test: errors
  test: select
diff --git a/src/test/regress/sql/create_am.sql b/src/test/regress/sql/create_am.sql
new file mode 100644
index ...e2051c5
*** a/src/test/regress/sql/create_am.sql
--- b/src/test/regress/sql/create_am.sql
***************
*** 0 ****
--- 1,73 ----
+ --
+ -- Create access method tests
+ --
+ 
+ -- Make gist2 over gisthandler. In fact, it would be a synonym to gist.
+ CREATE ACCESS METHOD gist2 TYPE INDEX HANDLER gisthandler;
+ 
+ -- Drop old index on fast_emp4000
+ DROP INDEX grect2ind;
+ 
+ -- Try to create gist2 index on fast_emp4000: fail because opclass doesn't exist
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ 
+ -- Make operator class for boxes using gist2
+ CREATE OPERATOR CLASS box_ops DEFAULT
+ 	FOR TYPE box USING gist2 AS
+ 	OPERATOR 1	<<,
+ 	OPERATOR 2	&<,
+ 	OPERATOR 3	&&,
+ 	OPERATOR 4	&>,
+ 	OPERATOR 5	>>,
+ 	OPERATOR 6	~=,
+ 	OPERATOR 7	@>,
+ 	OPERATOR 8	<@,
+ 	OPERATOR 9	&<|,
+ 	OPERATOR 10	<<|,
+ 	OPERATOR 11	|>>,
+ 	OPERATOR 12	|&>,
+ 	OPERATOR 13	~,
+ 	OPERATOR 14	@,
+ 	FUNCTION 1	gist_box_consistent(internal, box, smallint, oid, internal),
+ 	FUNCTION 2	gist_box_union(internal, internal),
+ 	FUNCTION 3	gist_box_compress(internal),
+ 	FUNCTION 4	gist_box_decompress(internal),
+ 	FUNCTION 5	gist_box_penalty(internal, internal, internal),
+ 	FUNCTION 6	gist_box_picksplit(internal, internal),
+ 	FUNCTION 7	gist_box_same(box, box, internal),
+ 	FUNCTION 9	gist_box_fetch(internal);
+ 
+ -- Create gist2 index on fast_emp4000
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ 
+ -- Now check the results from plain indexscan
+ SET enable_seqscan = OFF;
+ SET enable_indexscan = ON;
+ SET enable_bitmapscan = OFF;
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+ 
+ -- Try to drop access method: fail because of depending objects
+ DROP ACCESS METHOD gist2;
+ 
+ -- Drop access method cascade
+ DROP ACCESS METHOD gist2 CASCADE;
+ 
+ -- Reset optimizer options
+ RESET enable_seqscan;
+ RESET enable_indexscan;
+ RESET enable_bitmapscan;
generic-xlog.9.patchapplication/octet-stream; name=generic-xlog.9.patchDownload
diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
new file mode 100644
index c72a1f2..c0e38fd
*** a/src/backend/access/rmgrdesc/Makefile
--- b/src/backend/access/rmgrdesc/Makefile
*************** subdir = src/backend/access/rmgrdesc
*** 8,16 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o gindesc.o gistdesc.o \
! 	   hashdesc.o heapdesc.o mxactdesc.o nbtdesc.o relmapdesc.o \
! 	   replorigindesc.o seqdesc.o smgrdesc.o spgdesc.o \
  	   standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
  
  include $(top_srcdir)/src/backend/common.mk
--- 8,16 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o genericdesc.o \
! 	   gindesc.o gistdesc.o hashdesc.o heapdesc.o mxactdesc.o nbtdesc.o \
! 	   relmapdesc.o replorigindesc.o seqdesc.o smgrdesc.o spgdesc.o \
  	   standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
  
  include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/genericdesc.c b/src/backend/access/rmgrdesc/genericdesc.c
new file mode 100644
index ...3d035c2
*** a/src/backend/access/rmgrdesc/genericdesc.c
--- b/src/backend/access/rmgrdesc/genericdesc.c
***************
*** 0 ****
--- 1,58 ----
+ /*-------------------------------------------------------------------------
+  *
+  * genericdesc.c
+  *	  rmgr descriptor routines for access/transam/generic_xlog.c
+  *
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/backend/access/rmgrdesc/genericdesc.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/generic_xlog.h"
+ #include "lib/stringinfo.h"
+ #include "storage/relfilenode.h"
+ 
+ /*
+  * Description of generic xlog record: write page regions which this record
+  * overrides.
+  */
+ void
+ generic_desc(StringInfo buf, XLogReaderState *record)
+ {
+ 	Pointer		ptr = XLogRecGetData(record),
+ 				end = ptr + XLogRecGetDataLen(record);
+ 
+ 	while (ptr < end)
+ 	{
+ 		OffsetNumber	offset,
+ 						length;
+ 
+ 		memcpy(&offset, ptr, sizeof(offset));
+ 		ptr += sizeof(offset);
+ 		memcpy(&length, ptr, sizeof(length));
+ 		ptr += sizeof(length);
+ 		ptr += length;
+ 
+ 		if (ptr < end)
+ 			appendStringInfo(buf, "offset %u, length %u; ", offset, length);
+ 		else
+ 			appendStringInfo(buf, "offset %u, length %u", offset, length);
+ 	}
+ 
+ 	return;
+ }
+ 
+ /*
+  * Identification of generic xlog record: we don't distinguish any subtypes
+  * inside generic xlog records.
+  */
+ const char *
+ generic_identify(uint8 info)
+ {
+ 	return "Generic";
+ }
diff --git a/src/backend/access/transam/Makefile b/src/backend/access/transam/Makefile
new file mode 100644
index 94455b2..16fbe47
*** a/src/backend/access/transam/Makefile
--- b/src/backend/access/transam/Makefile
*************** subdir = src/backend/access/transam
*** 12,19 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = clog.o commit_ts.o multixact.o parallel.o rmgr.o slru.o subtrans.o \
! 	timeline.o transam.o twophase.o twophase_rmgr.o varsup.o \
  	xact.o xlog.o xlogarchive.o xlogfuncs.o \
  	xloginsert.o xlogreader.o xlogutils.o
  
--- 12,19 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = clog.o commit_ts.o generic_xlog.o multixact.o parallel.o rmgr.o slru.o \
! 	subtrans.o timeline.o transam.o twophase.o twophase_rmgr.o varsup.o \
  	xact.o xlog.o xlogarchive.o xlogfuncs.o \
  	xloginsert.o xlogreader.o xlogutils.o
  
diff --git a/src/backend/access/transam/generic_xlog.c b/src/backend/access/transam/generic_xlog.c
new file mode 100644
index ...ef4836b
*** a/src/backend/access/transam/generic_xlog.c
--- b/src/backend/access/transam/generic_xlog.c
***************
*** 0 ****
--- 1,452 ----
+ /*-------------------------------------------------------------------------
+  *
+  * generic_xlog.c
+  *	 Implementation of generic xlog records.
+  *
+  *
+  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/backend/access/transam/generic_xlog.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/generic_xlog.h"
+ #include "access/xlogutils.h"
+ #include "miscadmin.h"
+ #include "utils/memutils.h"
+ 
+ /*---
+  * Delta between pages consists of set of fragments.  Each fragment represents
+  * changes made in given region of page.  Fragment is described as following.
+  *
+  * - offset of page region (OffsetNumber)
+  * - length of page region (OffsetNumber)
+  * - data - the data to place into described region ('length' number of bytes)
+  *
+  * Unchanged regions of page are uncovered by these fragments.  This is why
+  * delta could be more compact than full page image.  But if unchanged region
+  * of page is less than fragment header (offset and length) then it would
+  * increase size of delta instead of decreasing.  Thus, we break fragment only
+  * for unchanged regions greater than MATCH_THRESHOLD.
+  *
+  * The worst case for delta size is when we didn't find any unchanged region
+  * in the page. Then size of delta would be size of page plus size of fragment
+  * header.
+  */
+ #define FRAGMENT_HEADER_SIZE	(2 * sizeof(OffsetNumber))
+ #define MATCH_THRESHOLD			FRAGMENT_HEADER_SIZE
+ #define MAX_DELTA_SIZE			BLCKSZ + FRAGMENT_HEADER_SIZE
+ 
+ /* Struct of generic xlog data for single page */
+ typedef struct
+ {
+ 	Buffer	buffer;			/* registered buffer */
+ 	char	image[BLCKSZ];	/* copy of page image for modification */
+ 	char	data[MAX_DELTA_SIZE]; /* delta between page images */
+ 	int		dataLen;		/* space consumed in data field */
+ 	bool	fullImage;		/* are we taking full image of this page? */
+ } PageData;
+ 
+ /* Enum of generic xlog (gxlog) status */
+ enum GenericXlogStatus
+ {
+ 	GXLOG_NOT_STARTED,	/* gxlog is not started */
+ 	GXLOG_LOGGED,		/* gxlog is started for logged relation */
+ 	GXLOG_UNLOGGED		/* gxlog is started for unlogged relation */
+ };
+ 
+ static enum GenericXlogStatus	genericXlogStatus = GXLOG_NOT_STARTED;
+ static PageData					pages[MAX_GENERIC_XLOG_PAGES];
+ 
+ 
+ static void writeFragment(PageData *pageData, OffsetNumber offset,
+ 						  OffsetNumber len, Pointer data);
+ static void writeDelta(PageData *pageData);
+ static void applyPageRedo(Page page, Pointer data, Size dataSize);
+ 
+ /*
+  * Write next fragment into delta.
+  */
+ static void
+ writeFragment(PageData *pageData, OffsetNumber offset, OffsetNumber length,
+ 			  Pointer data)
+ {
+ 	Pointer			ptr = pageData->data + pageData->dataLen;
+ 
+ 	/* Check we have enough of space */
+ 	Assert(pageData->dataLen + sizeof(offset) +
+ 		   sizeof(length) + length <= sizeof(pageData->data));
+ 
+ 	/* Write fragment data */
+ 	memcpy(ptr, &offset, sizeof(offset));
+ 	ptr += sizeof(offset);
+ 	memcpy(ptr, &length, sizeof(length));
+ 	ptr += sizeof(length);
+ 	memcpy(ptr, data, length);
+ 	ptr += length;
+ 
+ 	pageData->dataLen = ptr - pageData->data;
+ }
+ 
+ /*
+  * Make delta for given page.
+  */
+ static void
+ writeDelta(PageData *pageData)
+ {
+ 	Page			page = BufferGetPage(pageData->buffer),
+ 					image = (Page) pageData->image;
+ 	int				i,
+ 					fragmentBegin = -1,
+ 					fragmentEnd = -1;
+ 	uint16			pageLower = ((PageHeader) page)->pd_lower,
+ 					pageUpper = ((PageHeader) page)->pd_upper,
+ 					imageLower = ((PageHeader) image)->pd_lower,
+ 					imageUpper = ((PageHeader) image)->pd_upper;
+ 
+ 	for (i = 0; i < BLCKSZ; i++)
+ 	{
+ 		bool	match;
+ 
+ 		/*
+ 		 * Check if bytes in old and new page images matches.  We don't rely
+ 		 * data in unallocated area between pd_lower and pd_upper.  Thus we
+ 		 * assume unallocated area to expand with unmatched bytes.  Bytes
+ 		 * inside unallocated area are assumed to always match.
+ 		 */
+ 		if (i < pageLower)
+ 		{
+ 			if (i < imageLower)
+ 				match = (page[i] == image[i]);
+ 			else
+ 				match = false;
+ 		}
+ 		else if (i >= pageUpper)
+ 		{
+ 			if (i >= imageUpper)
+ 				match = (page[i] == image[i]);
+ 			else
+ 				match = false;
+ 		}
+ 		else
+ 		{
+ 			match = true;
+ 		}
+ 
+ 		if (match)
+ 		{
+ 			if (fragmentBegin >= 0)
+ 			{
+ 				/* Matched byte is potential of fragment. */
+ 				if (fragmentEnd < 0)
+ 					fragmentEnd = i;
+ 
+ 				/*
+ 				 * Write next fragment if sequence of matched bytes is longer
+ 				 * than MATCH_THRESHOLD.
+ 				 */
+ 				if (i - fragmentEnd >= MATCH_THRESHOLD)
+ 				{
+ 					writeFragment(pageData, fragmentBegin,
+ 								  fragmentEnd - fragmentBegin,
+ 								  page + fragmentBegin);
+ 					fragmentBegin = -1;
+ 					fragmentEnd = -1;
+ 				}
+ 			}
+ 		}
+ 		else
+ 		{
+ 			/* On unmatched byte, start new fragment if it's not done yet */
+ 			if (fragmentBegin < 0)
+ 				fragmentBegin = i;
+ 			fragmentEnd = -1;
+ 		}
+ 	}
+ 
+ 	if (fragmentBegin >= 0)
+ 		writeFragment(pageData, fragmentBegin,
+ 					  BLCKSZ - fragmentBegin,
+ 					  page + fragmentBegin);
+ 
+ #ifdef WAL_DEBUG
+ 	/*
+ 	 * If xlog debug is enabled then check produced delta.  Result of delta
+ 	 * application to saved image should be the same as current page state.
+ 	 */
+ 	if (XLOG_DEBUG)
+ 	{
+ 		char	tmp[BLCKSZ];
+ 		memcpy(tmp, image, BLCKSZ);
+ 		applyPageRedo(tmp, pageData->data, pageData->dataLen);
+ 		elog(ERROR, "result of generic xlog apply doesn't match");
+ 	}
+ #endif
+ }
+ 
+ /*
+  * Start new generic xlog record.
+  */
+ void
+ GenericXLogStart(Relation relation)
+ {
+ 	int i;
+ 
+ 	if (genericXlogStatus != GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogStart: generic xlog is already started")));
+ 
+ 	genericXlogStatus = RelationNeedsWAL(relation) ? GXLOG_LOGGED : GXLOG_UNLOGGED;
+ 
+ 	for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 	{
+ 		pages[i].buffer = InvalidBuffer;
+ 	}
+ }
+ 
+ /*
+  * Register new buffer for generic xlog record.
+  */
+ Page
+ GenericXLogRegister(Buffer buffer, bool isNew)
+ {
+ 	int block_id;
+ 
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogRegister: generic xlog isn't started")));
+ 
+ 	/* Place new buffer to unused slot in array */
+ 	for (block_id = 0; block_id < MAX_GENERIC_XLOG_PAGES; block_id++)
+ 	{
+ 		if (BufferIsInvalid(pages[block_id].buffer))
+ 		{
+ 			pages[block_id].buffer = buffer;
+ 			memcpy(pages[block_id].image, BufferGetPage(buffer), BLCKSZ);
+ 			pages[block_id].dataLen = 0;
+ 			pages[block_id].fullImage = isNew;
+ 			return (Page)pages[block_id].image;
+ 		}
+ 		else if (pages[block_id].buffer == buffer)
+ 		{
+ 			/* 
+ 			 * Buffer already registered.  Just return image which is already
+ 			 * prepared.
+ 			 */
+ 			return (Page)pages[block_id].image;
+ 		}
+ 	}
+ 
+ 	ereport(ERROR,
+ 			(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ 			 errmsg("GenericXLogRegister: maximum number of %d buffers is exceeded",
+ 					MAX_GENERIC_XLOG_PAGES)));
+ 
+ 	/* keep compiler quiet */
+ 	return NULL;
+ }
+ 
+ /*
+  * Unregister particular buffer for generic xlog record.
+  */
+ void
+ GenericXLogUnregister(Buffer buffer)
+ {
+ 	int block_id;
+ 
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogUnregister: generic xlog isn't started")));
+ 
+ 	/* Find block in array to unregister */
+ 	for (block_id = 0; block_id < MAX_GENERIC_XLOG_PAGES; block_id++)
+ 	{
+ 		if (pages[block_id].buffer == buffer)
+ 		{
+ 			/*
+ 			 * Preserve order of pages in array because it could matter for
+ 			 * concurrency.
+ 			 */
+ 			memmove(&pages[block_id], &pages[block_id + 1],
+ 					(MAX_GENERIC_XLOG_PAGES - block_id - 1) * sizeof(PageData));
+ 			pages[MAX_GENERIC_XLOG_PAGES - 1].buffer = InvalidBuffer;
+ 			return;
+ 		}
+ 	}
+ 
+ 	ereport(ERROR,
+ 			(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 			 errmsg("GenericXLogUnregister: registered buffer not found")));
+ }
+ 
+ /*
+  * Put all changes in registered buffers to generic xlog record.
+  */
+ XLogRecPtr
+ GenericXLogFinish(void)
+ {
+ 	XLogRecPtr lsn = InvalidXLogRecPtr;
+ 	int i;
+ 
+ 	if (genericXlogStatus == GXLOG_LOGGED)
+ 	{
+ 		/* Logged relation: make xlog record in critical section. */
+ 		START_CRIT_SECTION();
+ 		XLogBeginInsert();
+ 
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			char	tmp[BLCKSZ];
+ 
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 
+ 			/* Swap current and saved page image. */
+ 			memcpy(tmp, pages[i].image, BLCKSZ);
+ 			memcpy(pages[i].image, BufferGetPage(pages[i].buffer), BLCKSZ);
+ 			memcpy(BufferGetPage(pages[i].buffer), tmp, BLCKSZ);
+ 
+ 			if (pages[i].fullImage)
+ 			{
+ 				/* Full page image doesn't require anything special from us */
+ 				XLogRegisterBuffer(i, pages[i].buffer, REGBUF_FORCE_IMAGE);
+ 			}
+ 			else
+ 			{
+ 				/*
+ 				 * In normal node calculate delta and write use it as data
+ 				 * associated with this page.
+ 				 */
+ 				XLogRegisterBuffer(i, pages[i].buffer, REGBUF_STANDARD);
+ 				writeDelta(&pages[i]);
+ 				XLogRegisterBufData(i, pages[i].data, pages[i].dataLen);
+ 			}
+ 		}
+ 
+ 		/* Insert xlog record */
+ 		lsn = XLogInsert(RM_GENERIC_ID, 0);
+ 
+ 		/* Set LSN and make buffers dirty */
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 			PageSetLSN(BufferGetPage(pages[i].buffer), lsn);
+ 			MarkBufferDirty(pages[i].buffer);
+ 		}
+ 		END_CRIT_SECTION();
+ 	}
+ 	else if (genericXlogStatus == GXLOG_UNLOGGED)
+ 	{
+ 		/* Unlogged relation: skip xlog-related stuff */
+ 		START_CRIT_SECTION();
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 			memcpy(BufferGetPage(pages[i].buffer), pages[i].image, BLCKSZ);
+ 			MarkBufferDirty(pages[i].buffer);
+ 		}
+ 		END_CRIT_SECTION();
+ 	}
+ 	else
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogFinish: generic xlog isn't started")));
+ 	}
+ 
+ 	genericXlogStatus = GXLOG_NOT_STARTED;
+ 
+ 	return lsn;
+ }
+ 
+ /*
+  * Abort generic xlog record.
+  */
+ void
+ GenericXLogAbort(void)
+ {
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogAbort: generic xlog isn't started")));
+ 
+ 	genericXlogStatus = GXLOG_NOT_STARTED;
+ }
+ 
+ /*
+  * Apply delta to given page image.
+  */
+ static void
+ applyPageRedo(Page page, Pointer data, Size dataSize)
+ {
+ 	Pointer ptr = data, end = data + dataSize;
+ 
+ 	while (ptr < end)
+ 	{
+ 		OffsetNumber	offset,
+ 						length;
+ 
+ 		memcpy(&offset, ptr, sizeof(offset));
+ 		ptr += sizeof(offset);
+ 		memcpy(&length, ptr, sizeof(length));
+ 		ptr += sizeof(length);
+ 
+ 		memcpy(page + offset, ptr, length);
+ 
+ 		ptr += length;
+ 	}
+ }
+ 
+ /*
+  * Redo function for generic xlog record.
+  */
+ void
+ generic_redo(XLogReaderState *record)
+ {
+ 	uint8		block_id;
+ 	Buffer		buffers[MAX_GENERIC_XLOG_PAGES] = {InvalidBuffer};
+ 	XLogRecPtr	lsn = record->EndRecPtr;
+ 
+ 	Assert(record->max_block_id < MAX_GENERIC_XLOG_PAGES);
+ 
+ 	/* Interate over blocks */
+ 	for (block_id = 0; block_id <= record->max_block_id; block_id++)
+ 	{
+ 		XLogRedoAction action;
+ 
+ 		if (!XLogRecHasBlockRef(record, block_id))
+ 			continue;
+ 
+ 		action = XLogReadBufferForRedo(record, block_id, &buffers[block_id]);
+ 
+ 		/* Apply redo to given block if needed */
+ 		if (action == BLK_NEEDS_REDO)
+ 		{
+ 			Pointer	blockData;
+ 			Size	blockDataSize;
+ 			Page	page;
+ 
+ 			page = BufferGetPage(buffers[block_id]);
+ 			blockData = XLogRecGetBlockData(record, block_id, &blockDataSize);
+ 			applyPageRedo(page, blockData, blockDataSize);
+ 
+ 			PageSetLSN(page, lsn);
+ 			MarkBufferDirty(buffers[block_id]);
+ 		}
+ 	}
+ 
+ 	/* Changes are done: unlock and release all buffers */
+ 	for (block_id = 0; block_id <= record->max_block_id; block_id++)
+ 	{
+ 		if (BufferIsValid(buffers[block_id]))
+ 			UnlockReleaseBuffer(buffers[block_id]);
+ 	}
+ }
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
new file mode 100644
index 7c4d773..7b38c16
*** a/src/backend/access/transam/rmgr.c
--- b/src/backend/access/transam/rmgr.c
***************
*** 11,16 ****
--- 11,17 ----
  #include "access/commit_ts.h"
  #include "access/gin.h"
  #include "access/gist_private.h"
+ #include "access/generic_xlog.h"
  #include "access/hash.h"
  #include "access/heapam_xlog.h"
  #include "access/brin_xlog.h"
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
new file mode 100644
index 88c3a49..2d69dc2
*** a/src/backend/replication/logical/decode.c
--- b/src/backend/replication/logical/decode.c
*************** LogicalDecodingProcessRecord(LogicalDeco
*** 135,140 ****
--- 135,141 ----
  		case RM_BRIN_ID:
  		case RM_COMMIT_TS_ID:
  		case RM_REPLORIGIN_ID:
+ 		case RM_GENERIC_ID:
  			break;
  		case RM_NEXT_ID:
  			elog(ERROR, "unexpected RM_NEXT_ID rmgr_id: %u", (RmgrIds) XLogRecGetRmid(buf.record));
diff --git a/src/bin/pg_xlogdump/.gitignore b/src/bin/pg_xlogdump/.gitignore
new file mode 100644
index eebaf30..33a1acf
*** a/src/bin/pg_xlogdump/.gitignore
--- b/src/bin/pg_xlogdump/.gitignore
***************
*** 4,9 ****
--- 4,10 ----
  /clogdesc.c
  /committsdesc.c
  /dbasedesc.c
+ /genericdesc.c
  /gindesc.c
  /gistdesc.c
  /hashdesc.c
diff --git a/src/bin/pg_xlogdump/rmgrdesc.c b/src/bin/pg_xlogdump/rmgrdesc.c
new file mode 100644
index f9cd395..cff7e59
*** a/src/bin/pg_xlogdump/rmgrdesc.c
--- b/src/bin/pg_xlogdump/rmgrdesc.c
***************
*** 11,16 ****
--- 11,17 ----
  #include "access/brin_xlog.h"
  #include "access/clog.h"
  #include "access/commit_ts.h"
+ #include "access/generic_xlog.h"
  #include "access/gin.h"
  #include "access/gist_private.h"
  #include "access/hash.h"
diff --git a/src/include/access/generic_xlog.h b/src/include/access/generic_xlog.h
new file mode 100644
index ...9a10c2c
*** a/src/include/access/generic_xlog.h
--- b/src/include/access/generic_xlog.h
***************
*** 0 ****
--- 1,92 ----
+ /*-------------------------------------------------------------------------
+  *
+  * generic_xlog.h
+  *	  Generic xlog API definition.
+  *
+  *
+  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/access/generic_xlog.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef GENERIC_XLOG_H
+ #define GENERIC_XLOG_H
+ 
+ #include "access/xlog.h"
+ #include "access/xlog_internal.h"
+ #include "storage/bufpage.h"
+ #include "utils/rel.h"
+ 
+ #define MAX_GENERIC_XLOG_PAGES	  3
+ 
+ /*-------------------------------------------------------------------------
+  * API for construction of generic xlog records
+  *
+  * This API allows user to construct generic xlog records which are
+  * describing difference between pages in general way.  Thus it's useful
+  * for extension which provides custom access methods because they couldn't
+  * register their own WAL redo routines.
+  *
+  * Generic xlog record should be constructed in following steps.
+  * 1) GenericXLogStart(relation) - start construction of generic xlog
+  *	  record for given relation.
+  * 2) GenericXLogRegister(buffer, isNew) - register one or more buffers
+  *	  for generic xlog record.  This function return a copy of page image
+  *	  where modifications should be performed.  The second argument
+  *	  indicates that block is new and full image should be taken.
+  * 3) Do modification of page images obtained in previous step.
+  * 4) GenericXLogFinish() - finish construction of generic xlog record.
+  *
+  * Please, note following points while constructing generic xlog records.
+  * - No direct modifications of page images are allowed! All modifications
+  *	 should be done in copies returned by GenericXLogRegister().  Literally
+  *	 code which makes generic xlog records should never call
+  *	 BufferGetPage() function.
+  * - On any step generic xlog record construction could be canceled by
+  *	 calling GenericXLogAbort().  All changes made in page images copies
+  *	 would be discarded.
+  * - Registrations of buffers (step 2) and modifications of page images
+  *	 (step 3) could be mixed in any sequence.  The only restriction is that
+  *	 you can modify page image only after registration of corresponding
+  *	 buffer.
+  * - After registration buffer also can be unregistered by calling
+  *	 GenericXLogUnregister(buffer).  In this case changes made in particular
+  *	 page image copy will be discarded.
+  * - Generic xlog assumes that pages are using standard layout.  I.e. all
+  *	 information between pd_lower and pd_upper will be discarded.
+  * - Maximum number of buffers simultaneously registered for generic xlog
+  *	 is MAX_GENERIC_XLOG_PAGES.  Error would be thrown if this limit
+  *	 exceeded.
+  * - Since you modify copies of page images, GenericXLogStart() doesn't
+  *	 start a critical section.  Thus, you can do memory allocation, error
+  *	 throwing etc between GenericXLogStart() and GenericXLogFinish().
+  *	 Actual critical section present inside GenericXLogFinish().
+  * - GenericXLogFinish() takes care about marking buffers dirty and setting
+  *	 their LSNs.  You don't need to do this explicitly.
+  * - For unlogged relations, everything work the same expect there is no
+  *	 WAL record produced.  Thus, you typically don't need to do any explicit
+  *	 checks for unlogged relations.
+  * - If registered buffer isn't new, generic xlog record contains delta
+  *	 between old and new page images.  This delta is produced by per byte
+  *	 comparison.  Current delta mechanist is not effective for data shift
+  *	 inside the page.  However, it could be improved in further versions.
+  * - Generic xlog redo function will acquire exclusive locks to buffers
+  *	 in the same order they were registered.  After redo of all changes
+  *	 locks would be released in the same order.  That could makes sense for
+  *	 concurrency.
+  *-------------------------------------------------------------------------
+  */
+ extern void GenericXLogStart(Relation relation);
+ extern Page GenericXLogRegister(Buffer buffer, bool isNew);
+ extern void GenericXLogUnregister(Buffer buffer);
+ extern XLogRecPtr GenericXLogFinish(void);
+ extern void GenericXLogAbort(void);
+ 
+ /* functions defined for rmgr */
+ extern void generic_redo(XLogReaderState *record);
+ extern const char *generic_identify(uint8 info);
+ extern void generic_desc(StringInfo buf, XLogReaderState *record);
+ 
+ #endif   /* GENERIC_XLOG_H */
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
new file mode 100644
index fab912d..3cfe6f7
*** a/src/include/access/rmgrlist.h
--- b/src/include/access/rmgrlist.h
*************** PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo
*** 45,47 ****
--- 45,48 ----
  PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL)
  PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL)
  PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL)
+ PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL)
bloom-contrib.9.patchapplication/octet-stream; name=bloom-contrib.9.patchDownload
diff --git a/contrib/Makefile b/contrib/Makefile
new file mode 100644
index bd251f6..3ac3818
*** a/contrib/Makefile
--- b/contrib/Makefile
*************** SUBDIRS = \
*** 8,13 ****
--- 8,14 ----
  		adminpack	\
  		auth_delay	\
  		auto_explain	\
+ 		bloom		\
  		btree_gin	\
  		btree_gist	\
  		chkpass		\
diff --git a/contrib/bloom/.gitignore b/contrib/bloom/.gitignore
new file mode 100644
index ...5dcb3ff
*** a/contrib/bloom/.gitignore
--- b/contrib/bloom/.gitignore
***************
*** 0 ****
--- 1,4 ----
+ # Generated subdirectories
+ /log/
+ /results/
+ /tmp_check/
diff --git a/contrib/bloom/Makefile b/contrib/bloom/Makefile
new file mode 100644
index ...13bd397
*** a/contrib/bloom/Makefile
--- b/contrib/bloom/Makefile
***************
*** 0 ****
--- 1,24 ----
+ # contrib/bloom/Makefile
+ 
+ MODULE_big = bloom
+ OBJS = blcost.o blinsert.o blscan.o blutils.o blvacuum.o blvalidate.o $(WIN32RES)
+ 
+ EXTENSION = bloom
+ DATA = bloom--1.0.sql
+ PGFILEDESC = "bloom access method - signature file based index"
+ 
+ REGRESS = bloom
+ 
+ ifdef USE_PGXS
+ PG_CONFIG = pg_config
+ PGXS := $(shell $(PG_CONFIG) --pgxs)
+ include $(PGXS)
+ else
+ subdir = contrib/bloom
+ top_builddir = ../..
+ include $(top_builddir)/src/Makefile.global
+ include $(top_srcdir)/contrib/contrib-global.mk
+ endif
+ 
+ wal-check: temp-install
+ 	$(prove_check)
diff --git a/contrib/bloom/blcost.c b/contrib/bloom/blcost.c
new file mode 100644
index ...d918dce
*** a/contrib/bloom/blcost.c
--- b/contrib/bloom/blcost.c
***************
*** 0 ****
--- 1,45 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blcost.c
+  *		Cost estimate function for bloom indexes.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blcost.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "fmgr.h"
+ #include "optimizer/cost.h"
+ #include "utils/selfuncs.h"
+ 
+ #include "bloom.h"
+ 
+ void
+ blcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
+ 			   Cost *indexStartupCost, Cost *indexTotalCost,
+ 			   Selectivity *indexSelectivity, double *indexCorrelation)
+ {
+ 	IndexOptInfo   *index = path->indexinfo;
+ 	List		   *qinfos;
+ 	GenericCosts	costs;
+ 
+ 	/* Do preliminary analysis of indexquals */
+ 	qinfos = deconstruct_indexquals(path);
+ 
+ 	MemSet(&costs, 0, sizeof(costs));
+ 
+ 	/* We have to visit all index tuples anyway */
+ 	costs.numIndexTuples = index->tuples;
+ 
+ 	/* Use generic estimate */
+ 	genericcostestimate(root, path, loop_count, qinfos, &costs);
+ 
+ 	*indexStartupCost = costs.indexStartupCost;
+ 	*indexTotalCost = costs.indexTotalCost;
+ 	*indexSelectivity = costs.indexSelectivity;
+ 	*indexCorrelation = costs.indexCorrelation;
+ }
diff --git a/contrib/bloom/blinsert.c b/contrib/bloom/blinsert.c
new file mode 100644
index ...15bff40
*** a/contrib/bloom/blinsert.c
--- b/contrib/bloom/blinsert.c
***************
*** 0 ****
--- 1,293 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blinsert.c
+  *		Bloom index build and insert functions.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blinsert.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "access/generic_xlog.h"
+ #include "catalog/index.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ 
+ #include "bloom.h"
+ 
+ PG_MODULE_MAGIC;
+ 
+ typedef struct
+ {
+ 	BloomState		blstate;
+ 	MemoryContext	tmpCtx;
+ 	char			data[BLCKSZ];
+ 	int64			count;
+ } BloomBuildState;
+ 
+ /*
+  * Flush page cached in BloomBuildState.
+  */
+ static void
+ flushCachedPage(Relation index, BloomBuildState *buildstate)
+ {
+ 	Page	page;
+ 	Buffer	buffer = BloomNewBuffer(index);
+ 
+ 	GenericXLogStart(index);
+ 	page = GenericXLogRegister(buffer, true);
+ 	memcpy(page, buildstate->data, BLCKSZ);
+ 	GenericXLogFinish();
+ 	UnlockReleaseBuffer(buffer);
+ }
+ 
+ /*
+  * (Re)initialize cached page in BloomBuildState.
+  */
+ static void
+ initCachedPage(BloomBuildState *buildstate)
+ {
+ 	memset(buildstate->data, 0, BLCKSZ);
+ 	BloomInitPage(buildstate->data, 0);
+ 	buildstate->count = 0;
+ }
+ 
+ /*
+  * Per-tuple callback from IndexBuildHeapScan.
+  */
+ static void
+ bloomBuildCallback(Relation index, HeapTuple htup, Datum *values,
+ 					bool *isnull, bool tupleIsAlive, void *state)
+ {
+ 	BloomBuildState	*buildstate = (BloomBuildState *)state;
+ 	MemoryContext	 oldCtx;
+ 	BloomTuple		*itup;
+ 
+ 	oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
+ 
+ 	itup = BloomFormTuple(&buildstate->blstate, &htup->t_self, values, isnull);
+ 
+ 	/* Try to add next item to cached page */
+ 	if (BloomPageAddItem(&buildstate->blstate, buildstate->data, itup) == false)
+ 	{
+ 		/* Cached page is full, flush it out and make a new one */
+ 		flushCachedPage(index, buildstate);
+ 
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		initCachedPage(buildstate);
+ 
+ 		if (BloomPageAddItem(&buildstate->blstate, buildstate->data, itup) == false)
+ 		{
+ 			/* We shouldn't be here since we're inserting to the empty page */
+ 			elog(ERROR, "can not add new tuple");
+ 		}
+ 	}
+ 	else
+ 	{
+ 		buildstate->count++;
+ 	}
+ 
+ 	MemoryContextSwitchTo(oldCtx);
+ 	MemoryContextReset(buildstate->tmpCtx);
+ }
+ 
+ IndexBuildResult *
+ blbuild(Relation heap, Relation index, IndexInfo *indexInfo)
+ {
+ 	IndexBuildResult   *result;
+ 	double				reltuples;
+ 	BloomBuildState		buildstate;
+ 
+ 	if (RelationGetNumberOfBlocks(index) != 0)
+ 		elog(ERROR, "index \"%s\" already contains data",
+ 			RelationGetRelationName(index));
+ 
+ 	/* Initialize the meta page */
+ 	BloomInitMetapage(index);
+ 
+ 	initBloomState(&buildstate.blstate, index);
+ 
+ 	buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
+ 												"Bloom build temporary context",
+ 												ALLOCSET_DEFAULT_MINSIZE,
+ 												ALLOCSET_DEFAULT_INITSIZE,
+ 												ALLOCSET_DEFAULT_MAXSIZE);
+ 
+ 	initCachedPage(&buildstate);
+ 
+ 	/* Do the heap scan */
+ 	reltuples = IndexBuildHeapScan(heap, index, indexInfo, true,
+ 									bloomBuildCallback, (void *) &buildstate);
+ 
+ 	if (buildstate.count > 0)
+ 		flushCachedPage(index, &buildstate);
+ 
+ 	MemoryContextDelete(buildstate.tmpCtx);
+ 
+ 	result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
+ 	result->heap_tuples = result->index_tuples = reltuples;
+ 
+ 	return result;
+ }
+ 
+ void
+ blbuildempty(Relation index)
+ {
+ 	if (RelationGetNumberOfBlocks(index) != 0)
+ 		elog(ERROR, "index \"%s\" already contains data",
+ 			RelationGetRelationName(index));
+ 
+ 	/* Initialize the meta page */
+ 	BloomInitMetapage(index);
+ }
+ 
+ bool
+ blinsert(Relation index, Datum *values, bool *isnull,
+ 		ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique)
+ {
+ 	BloomState			blstate;
+ 	BloomTuple		   *itup;
+ 	MemoryContext		oldCtx;
+ 	MemoryContext		insertCtx;
+ 	BloomMetaPageData  *metaData;
+ 	Buffer				buffer,
+ 						metaBuffer;
+ 	Page				page,
+ 						metaPage;
+ 	BlockNumber			blkno = InvalidBlockNumber;
+ 	OffsetNumber		nStart;
+ 
+ 	insertCtx = AllocSetContextCreate(CurrentMemoryContext,
+ 										"Bloom insert temporary context",
+ 										ALLOCSET_DEFAULT_MINSIZE,
+ 										ALLOCSET_DEFAULT_INITSIZE,
+ 										ALLOCSET_DEFAULT_MAXSIZE);
+ 
+ 	oldCtx = MemoryContextSwitchTo(insertCtx);
+ 
+ 	initBloomState(&blstate, index);
+ 	itup = BloomFormTuple(&blstate, ht_ctid, values, isnull);
+ 
+ 	/*
+ 	 * At first, try to insert new tuple to the first page in
+ 	 * notFullPage array.  If success we don't need to modify the
+ 	 * meta page.
+ 	 */
+ 	metaBuffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 	LockBuffer(metaBuffer, BUFFER_LOCK_SHARE);
+ 	metaData = BloomPageGetMeta(BufferGetPage(metaBuffer));
+ 
+ 	if (metaData->nEnd > metaData->nStart)
+ 	{
+ 		Page	page;
+ 
+ 		blkno = metaData->notFullPage[metaData->nStart];
+ 
+ 		Assert(blkno != InvalidBlockNumber);
+ 		LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		GenericXLogStart(index);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		if (BloomPageAddItem(&blstate, page, itup))
+ 		{
+ 			GenericXLogFinish();
+ 			UnlockReleaseBuffer(buffer);
+ 			ReleaseBuffer(metaBuffer);
+ 			goto away;
+ 		}
+ 		else
+ 		{
+ 			GenericXLogAbort();
+ 			UnlockReleaseBuffer(buffer);
+ 		}
+ 	}
+ 	else
+ 	{
+ 		/* First page in notFullPage isn't suitable */
+ 		LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
+ 	}
+ 
+ 	/*
+ 	 * Try other pages in notFullPage array.  We will have to change nStart
+ 	 * in metapage.  Thus, grab exclusive lock on metapage.
+ 	 */
+ 	LockBuffer(metaBuffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 	GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(metaBuffer, false);
+ 	metaData = BloomPageGetMeta(metaPage);
+ 
+ 	/*
+ 	 * Iterate over notFullPage array.  Skip page we already tried first.
+ 	 */
+ 	nStart = metaData->nStart;
+ 	if (metaData->nEnd > nStart &&
+ 		blkno == metaData->notFullPage[nStart] )
+ 		nStart++;
+ 
+ 	while (metaData->nEnd > nStart)
+ 	{
+ 		blkno = metaData->notFullPage[nStart];
+ 		Assert(blkno != InvalidBlockNumber);
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		if (BloomPageAddItem(&blstate, page, itup))
+ 		{
+ 			metaData->nStart = nStart;
+ 			GenericXLogFinish();
+ 			UnlockReleaseBuffer(buffer);
+ 			UnlockReleaseBuffer(metaBuffer);
+ 			goto away;
+ 		}
+ 		else
+ 		{
+ 			GenericXLogUnregister(buffer);
+ 			UnlockReleaseBuffer(buffer);
+ 		}
+ 		nStart++;
+ 	}
+ 
+ 	GenericXLogAbort();
+ 
+ 	/*
+ 	 * Didn't find place to insert in notFullPage array.  Allocate new page.
+ 	 */
+ 	buffer = BloomNewBuffer(index);
+ 
+ 	GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(metaBuffer, false);
+ 	metaData = BloomPageGetMeta(metaPage);
+ 	page = GenericXLogRegister(buffer, true);
+ 	BloomInitPage(page, 0);
+ 	BloomPageAddItem(&blstate, page, itup);
+ 
+ 	metaData->nStart = 0;
+ 	metaData->nEnd = 1;
+ 	metaData->notFullPage[ 0 ] = BufferGetBlockNumber(buffer);
+ 
+ 	GenericXLogFinish();
+ 
+ 	UnlockReleaseBuffer(buffer);
+ 	UnlockReleaseBuffer(metaBuffer);
+ 
+ away:
+ 	MemoryContextSwitchTo(oldCtx);
+ 	MemoryContextDelete(insertCtx);
+ 
+ 	return false;
+ }
diff --git a/contrib/bloom/bloom--1.0.sql b/contrib/bloom/bloom--1.0.sql
new file mode 100644
index ...7fa7513
*** a/contrib/bloom/bloom--1.0.sql
--- b/contrib/bloom/bloom--1.0.sql
***************
*** 0 ****
--- 1,19 ----
+ CREATE OR REPLACE FUNCTION blhandler(internal)
+ RETURNS index_am_handler
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C;
+ 
+ -- Access method
+ CREATE ACCESS METHOD bloom TYPE INDEX HANDLER blhandler;
+ 
+ -- Opclasses
+ 
+ CREATE OPERATOR CLASS int4_ops
+ DEFAULT FOR TYPE int4 USING bloom AS
+ 	OPERATOR	1	=(int4, int4),
+ 	FUNCTION	1	hashint4(int4);
+ 
+ CREATE OPERATOR CLASS text_ops
+ DEFAULT FOR TYPE text USING bloom AS
+ 	OPERATOR	1	=(text, text),
+ 	FUNCTION	1	hashtext(text);
diff --git a/contrib/bloom/bloom.control b/contrib/bloom/bloom.control
new file mode 100644
index ...4d4124b
*** a/contrib/bloom/bloom.control
--- b/contrib/bloom/bloom.control
***************
*** 0 ****
--- 1,5 ----
+ # bloom extension
+ comment = 'bloom access method - signature file based index'
+ default_version = '1.0'
+ module_pathname = '$libdir/bloom'
+ relocatable = true
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
new file mode 100644
index ...6f20ee4
*** a/contrib/bloom/bloom.h
--- b/contrib/bloom/bloom.h
***************
*** 0 ****
--- 1,176 ----
+ /*-------------------------------------------------------------------------
+  *
+  * bloom.h
+  *	  Header for bloom index.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/bloom.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef _BLOOM_H_
+ #define _BLOOM_H_
+ 
+ #include "access/amapi.h"
+ #include "access/generic_xlog.h"
+ #include "access/itup.h"
+ #include "access/xlog.h"
+ #include "nodes/relation.h"
+ #include "fmgr.h"
+ 
+ /* Support procedures numbers */
+ #define	BLOOM_HASH_PROC			1
+ #define	BLOOM_NPROC				1
+ 
+ /* Scan strategies */
+ #define	BLOOM_EQUAL_STRATEGY	1
+ #define	BLOOM_NSTRATEGIES		1
+ 
+ /* Opaque for bloom pages */
+ typedef struct BloomPageOpaqueData
+ {
+ 	OffsetNumber	maxoff;
+ 	uint16			flags;
+ } BloomPageOpaqueData;
+ 
+ typedef BloomPageOpaqueData *BloomPageOpaque;
+ 
+ /* Bloom page flags */
+ #define BLOOM_META		(1<<0)
+ #define BLOOM_DELETED	(2<<0)
+ 
+ #define BloomPageGetOpaque(page) ((BloomPageOpaque) PageGetSpecialPointer(page))
+ #define BloomPageGetMaxOffset(page) (BloomPageGetOpaque(page)->maxoff)
+ #define BloomPageIsMeta(page) (BloomPageGetOpaque(page)->flags & BLOOM_META)
+ #define BloomPageIsDeleted(page) (BloomPageGetOpaque(page)->flags & BLOOM_DELETED)
+ #define BloomPageSetDeleted(page) (BloomPageGetOpaque(page)->flags |= BLOOM_DELETED)
+ #define BloomPageSetNonDeleted(page) (BloomPageGetOpaque(page)->flags &= ~BLOOM_DELETED)
+ #define BloomPageGetData(page)		((BloomTuple *)PageGetContents(page))
+ #define BloomPageGetTuple(state, page, offset) \
+ 	((BloomTuple *)(PageGetContents(page) \
+ 		+ (state)->sizeOfBloomTuple * ((offset) - 1)))
+ #define BloomPageGetNextTuple(state, tuple) \
+ 	((BloomTuple *)((Pointer)(tuple) + (state)->sizeOfBloomTuple))
+ 
+ /* Preserved page numbers */
+ #define BLOOM_METAPAGE_BLKNO	(0)
+ #define BLOOM_HEAD_BLKNO		(1) /* first data page */
+ 
+ /* Bloom index options */
+ typedef struct BloomOptions
+ {
+ 	int32	vl_len_;				 /* varlena header (do not touch directly!) */
+ 	int		bloomLength;			 /* length of signature in uint16 */
+ 	int		bitSize[INDEX_MAX_KEYS]; /* signature bits per index key */
+ } BloomOptions;
+ 
+ /*
+  * FreeBlockNumberArray - array of block numbers sized so that metadata fill
+  * all space in metapage.
+  */
+ typedef BlockNumber FreeBlockNumberArray[
+ 	MAXALIGN_DOWN(
+ 		BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(BloomPageOpaqueData))
+ 			   - MAXALIGN(sizeof(uint16) * 2 + sizeof(uint32) + sizeof(BloomOptions))
+ 	) / sizeof(BlockNumber) 
+ ];
+ 
+ /* Metadata of bloom index */
+ typedef struct BloomMetaPageData
+ {
+ 	uint32					magickNumber;
+ 	uint16					nStart;
+ 	uint16					nEnd;
+ 	BloomOptions			opts;
+ 	FreeBlockNumberArray	notFullPage;
+ } BloomMetaPageData;
+ 
+ /* Magic number to distinguish bloom pages among anothers */
+ #define BLOOM_MAGICK_NUMBER	(0xDBAC0DED)
+ 
+ /* Number of blocks numbers fit in BloomMetaPageData */
+ #define BloomMetaBlockN		(sizeof(FreeBlockNumberArray) / sizeof(BlockNumber))
+ 
+ #define BloomPageGetMeta(page)	((BloomMetaPageData *) PageGetContents(page))
+ 
+ typedef struct BloomState 
+ {
+ 	FmgrInfo			hashFn[INDEX_MAX_KEYS];
+ 	BloomOptions	   *opts; /* stored in rd_amcache and defined at creation time */
+ 	int32				nColumns;
+ 	/* 
+ 	 * sizeOfBloomTuple is index's specific, and it depends on
+ 	 * reloptions, so precompute it
+ 	 */
+ 	int32				sizeOfBloomTuple; 
+ } BloomState;
+ 
+ #define BloomPageGetFreeSpace(state, page) \
+ 	(BLCKSZ - MAXALIGN(SizeOfPageHeaderData) \
+ 		- BloomPageGetMaxOffset(page) * (state)->sizeOfBloomTuple \
+ 		- MAXALIGN(sizeof(BloomPageOpaqueData)))
+ 
+ /*
+  * Tuples are very different from all other relations
+  */
+ typedef uint16	SignType;
+ 
+ typedef struct BloomTuple
+ {
+ 	ItemPointerData		heapPtr;
+ 	SignType			sign[1];
+ } BloomTuple;
+ 
+ #define BLOOMTUPLEHDRSZ	offsetof(BloomTuple, sign)
+ 
+ /* Opaque data structure for bloom index scan */
+ typedef struct BloomScanOpaqueData
+ {
+ 	SignType   *sign;	/* Scan signature */
+ 	BloomState	state;
+ } BloomScanOpaqueData;
+ 
+ typedef BloomScanOpaqueData *BloomScanOpaque;
+ 
+ /* blutils.c */
+ extern void _PG_init(void);
+ extern Datum blhandler(PG_FUNCTION_ARGS);
+ extern void initBloomState(BloomState *state, Relation index);
+ extern void BloomInitMetapage(Relation index);
+ extern void BloomInitPage(Page page, uint16 flags);
+ extern Buffer BloomNewBuffer(Relation index);
+ extern void signValue(BloomState *state, SignType *sign, Datum value, int attno);
+ extern BloomTuple* BloomFormTuple(BloomState *state, ItemPointer iptr, Datum *values, bool *isnull);
+ extern bool BloomPageAddItem(BloomState *state, Page page, BloomTuple *tuple);
+ 
+ /* blvalidate.c */
+ extern bool blvalidate(Oid opclassoid);
+ 
+ /* interface functions */
+ extern bool blinsert(Relation index, Datum *values, bool *isnull,
+ 					 ItemPointer ht_ctid, Relation heapRel,
+ 					 IndexUniqueCheck checkUnique);
+ extern IndexScanDesc blbeginscan(Relation r, int nkeys, int norderbys);
+ extern int64 blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
+ extern void blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ 					 ScanKey orderbys, int norderbys);
+ extern void blendscan(IndexScanDesc scan);
+ extern void blmarkpos(IndexScanDesc scan);
+ extern void blrestrpos(IndexScanDesc scan);
+ extern IndexBuildResult *blbuild(Relation heap, Relation index,
+ 								   struct IndexInfo *indexInfo);
+ extern void blbuildempty(Relation index);
+ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
+ 	IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback,
+ 	void *callback_state);
+ extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
+ 	IndexBulkDeleteResult *stats);
+ extern bytea *bloptions(Datum reloptions, bool validate);
+ extern void blcostestimate(PlannerInfo *root, IndexPath *path,
+ 						   double loop_count, Cost *indexStartupCost,
+ 						   Cost *indexTotalCost, Selectivity *indexSelectivity,
+ 						   double *indexCorrelation);
+ 
+ #endif
diff --git a/contrib/bloom/blscan.c b/contrib/bloom/blscan.c
new file mode 100644
index ...467a1b1
*** a/contrib/bloom/blscan.c
--- b/contrib/bloom/blscan.c
***************
*** 0 ****
--- 1,174 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blscan.c
+  *		Bloom index scan functions.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blscan.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/relscan.h"
+ #include "pgstat.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/lmgr.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ 
+ #include "bloom.h"
+ 
+ IndexScanDesc
+ blbeginscan(Relation r, int nkeys, int norderbys)
+ {
+ 	IndexScanDesc scan;
+ 
+ 	scan = RelationGetIndexScan(r, nkeys, norderbys);
+ 
+ 	return scan;
+ }
+ 
+ void
+ blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ 							 ScanKey orderbys, int norderbys)
+ {
+ 	BloomScanOpaque so;
+ 
+ 	so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so == NULL)
+ 	{
+ 		/* if called from blbeginscan */
+ 		so = (BloomScanOpaque) palloc(sizeof(BloomScanOpaqueData));
+ 		initBloomState(&so->state, scan->indexRelation);
+ 		scan->opaque = so;
+ 
+ 	}
+ 	else
+ 	{
+ 		if (so->sign)
+ 			pfree(so->sign);
+ 	}
+ 	so->sign = NULL;
+ 
+ 	if (scankey && scan->numberOfKeys > 0)
+ 	{
+ 		memmove(scan->keyData, scankey,
+ 				scan->numberOfKeys * sizeof(ScanKeyData));
+ 	}
+ }
+ 
+ void
+ blendscan(IndexScanDesc scan)
+ {
+ 	BloomScanOpaque so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so->sign)
+ 		pfree(so->sign);
+ 	so->sign = NULL;
+ }
+ 
+ void
+ blmarkpos(IndexScanDesc scan)
+ {
+ 	elog(ERROR, "Bloom does not support mark/restore");
+ }
+ 
+ void
+ blrestrpos(IndexScanDesc scan)
+ {
+ 	elog(ERROR, "Bloom does not support mark/restore");
+ }
+ 
+ int64
+ blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
+ {
+ 	int64					ntids = 0;
+ 	BlockNumber				blkno = BLOOM_HEAD_BLKNO,
+ 							npages;
+ 	int						i;
+ 	BufferAccessStrategy	bas;
+ 	BloomScanOpaque 		so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so->sign == NULL && scan->numberOfKeys > 0)
+ 	{
+ 		/* New search: have to calculate search signature */
+ 		ScanKey skey = scan->keyData;
+ 
+ 		so->sign = palloc0(sizeof(SignType) * so->state.opts->bloomLength); 
+ 		
+ 		for(i = 0; i < scan->numberOfKeys; i++)
+ 		{
+ 			/*
+ 			 * Assume bloom-indexable operators to be strict, so nothing
+ 			 * could be found for NULL key.
+ 			 */
+ 			if (skey->sk_flags & SK_ISNULL)
+ 			{
+ 				pfree(so->sign);
+ 				so->sign = NULL;
+ 				return 0;
+ 			}
+ 
+ 			/* Add next value to the signature */
+ 			signValue(&so->state, so->sign, skey->sk_argument,
+ 					  skey->sk_attno - 1);
+ 
+ 			skey++;
+ 		}
+ 	}
+ 
+ 	/*
+ 	 * We're going to read the whole index. This is why we use appropriate
+ 	 * buffer access strategy.
+ 	 */
+ 	bas = GetAccessStrategy(BAS_BULKREAD);
+ 	npages = RelationGetNumberOfBlocks(scan->indexRelation);
+ 
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		Buffer 			buffer;
+ 		Page			page;
+ 
+ 		buffer = ReadBufferExtended(scan->indexRelation, MAIN_FORKNUM,
+ 									blkno, RBM_NORMAL, bas);
+ 
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 		page = BufferGetPage(buffer);
+ 
+ 		if (!BloomPageIsDeleted(page))
+ 		{
+ 			OffsetNumber offset, maxOffset = BloomPageGetMaxOffset(page);
+ 
+ 			for (offset = 1; offset <= maxOffset; offset++)
+ 			{
+ 				BloomTuple *itup = BloomPageGetTuple(&so->state, page, offset);
+ 				bool		res = true;
+ 
+ 				/* Check index signature with scan signature */
+ 				for (i = 0; res && i < so->state.opts->bloomLength; i++)
+ 				{
+ 					if ((itup->sign[i] & so->sign[i]) != so->sign[i])
+ 						res = false;
+ 				}
+ 
+ 				/* Add matching tuples to bitmap */
+ 				if (res)
+ 				{
+ 					tbm_add_tuples(tbm, &itup->heapPtr, 1, true);
+ 					ntids++;
+ 				}
+ 			}
+ 		}
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 		CHECK_FOR_INTERRUPTS();
+ 	}
+ 	FreeAccessStrategy(bas);
+ 
+ 	return ntids;
+ }
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
new file mode 100644
index ...a7ec0c5
*** a/contrib/bloom/blutils.c
--- b/contrib/bloom/blutils.c
***************
*** 0 ****
--- 1,390 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blutils.c
+  *		Bloom index utilities.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blutils.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/amapi.h"
+ #include "access/generic_xlog.h"
+ #include "catalog/index.h"
+ #include "storage/lmgr.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "utils/memutils.h"
+ #include "access/reloptions.h"
+ #include "storage/freespace.h"
+ #include "storage/indexfsm.h"
+ 
+ #include "bloom.h"
+ 
+ /* Signature dealing macros */
+ #define BITSIGNTYPE	(BITS_PER_BYTE * sizeof(SignType))
+ #define GETWORD(x,i) ( *( (SignType*)(x) + (int)( (i) / BITSIGNTYPE ) ) )
+ #define CLRBIT(x,i)   GETWORD(x,i) &= ~( 0x01 << ( (i) % BITSIGNTYPE ) )
+ #define SETBIT(x,i)   GETWORD(x,i) |=  ( 0x01 << ( (i) % BITSIGNTYPE ) )
+ #define GETBIT(x,i) ( (GETWORD(x,i) >> ( (i) % BITSIGNTYPE )) & 0x01 )
+ 
+ PG_FUNCTION_INFO_V1(blhandler);
+ 
+ static relopt_kind bl_relopt_kind;
+ 
+ /*
+  * Module initialize function: initilized relation options.
+  */
+ void 
+ _PG_init(void)
+ {
+ 	int		i;
+ 	char	buf[16];
+ 
+ 	bl_relopt_kind = add_reloption_kind();
+ 
+ 	add_int_reloption(bl_relopt_kind, "length",
+ 					  "Length of signature in uint16 type", 5, 1, 256);
+ 
+ 	for(i = 0; i < INDEX_MAX_KEYS;i ++)
+ 	{
+ 		snprintf(buf, 16, "col%d", i+1);
+ 		add_int_reloption(bl_relopt_kind, buf,
+ 						  "Number of bits for corresponding column", 2, 1, 2048);
+ 	}
+ }
+ 
+ /*
+  * Bloom handler function: return IndexAmRoutine with access method parameters
+  * and callbacks.
+  */
+ Datum
+ blhandler(PG_FUNCTION_ARGS)
+ {
+ 	IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
+ 
+ 	amroutine->amstrategies = 1;
+ 	amroutine->amsupport = 1;
+ 	amroutine->amcanorder = false;
+ 	amroutine->amcanorderbyop = false;
+ 	amroutine->amcanbackward = false;
+ 	amroutine->amcanunique = false;
+ 	amroutine->amcanmulticol = true;
+ 	amroutine->amoptionalkey = true;
+ 	amroutine->amsearcharray = false;
+ 	amroutine->amsearchnulls = false;
+ 	amroutine->amstorage = false;
+ 	amroutine->amclusterable = false;
+ 	amroutine->ampredlocks = false;
+ 	amroutine->amkeytype = 0;
+ 
+ 	amroutine->aminsert = blinsert;
+ 	amroutine->ambeginscan = blbeginscan;
+ 	amroutine->amgettuple = NULL;
+ 	amroutine->amgetbitmap = blgetbitmap;
+ 	amroutine->amrescan = blrescan;
+ 	amroutine->amendscan = blendscan;
+ 	amroutine->ammarkpos = blmarkpos;
+ 	amroutine->amrestrpos = blrestrpos;
+ 	amroutine->ambuild = blbuild;
+ 	amroutine->ambuildempty = blbuildempty;
+ 	amroutine->ambulkdelete = blbulkdelete;
+ 	amroutine->amvacuumcleanup = blvacuumcleanup;
+ 	amroutine->amcanreturn = NULL;
+ 	amroutine->amcostestimate = blcostestimate;
+ 	amroutine->amoptions = bloptions;
+ 	amroutine->amvalidate = blvalidate;
+ 
+ 	PG_RETURN_POINTER(amroutine);
+ }
+ 
+ void 
+ initBloomState(BloomState *state, Relation index)
+ {
+ 	int	i;
+ 
+ 	state->nColumns = index->rd_att->natts;
+ 
+ 	/* Initialize hash function for each attribute */
+ 	for (i = 0; i < index->rd_att->natts; i++)
+ 	{
+ 		fmgr_info_copy(&(state->hashFn[i]),
+ 						index_getprocinfo(index, i + 1, BLOOM_HASH_PROC),
+ 						CurrentMemoryContext);
+ 	}
+ 
+ 	/* Inititalize amcache if needed */
+ 	if (!index->rd_amcache)
+ 	{
+ 		Buffer				buffer;
+ 		Page				page;
+ 		BloomMetaPageData	*meta;
+ 		BloomOptions		*opts;
+ 
+ 		opts = MemoryContextAlloc(index->rd_indexcxt, sizeof(BloomOptions));
+ 
+ 		buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 
+ 		page = BufferGetPage(buffer);
+ 
+ 		if (!BloomPageIsMeta(page))
+ 			elog(ERROR, "Relation is not a bloom index");
+ 		meta = BloomPageGetMeta(BufferGetPage(buffer));
+ 
+ 		if (meta->magickNumber != BLOOM_MAGICK_NUMBER)
+ 			elog(ERROR, "Relation is not a bloom index");
+ 
+ 		*opts = meta->opts;
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 
+ 		index->rd_amcache = (void *)opts;
+ 	}
+ 
+ 	state->opts = (BloomOptions *)index->rd_amcache;
+ 	state->sizeOfBloomTuple = BLOOMTUPLEHDRSZ +
+ 									sizeof(SignType) * state->opts->bloomLength; 
+ }
+ 
+ /*
+  * Add bits of given value to the signature.
+  */
+ void
+ signValue(BloomState *state, SignType *sign, Datum value, int attno)
+ {
+ 	uint32		hashVal;
+ 	int 		nBit, j;
+ 
+ 	/*
+ 	 * init generator with "column's" number to get
+ 	 * "hashed" seed for new value. We don't want to map
+ 	 * the same numbers from different columns into the same bits!
+ 	 */
+ 	srand(attno);
+ 
+ 	/*
+ 	 * Init hash sequence to map our value into bits. the same values
+ 	 * in different columns will be mapped into different bits because
+ 	 * of step above
+ 	 */
+ 	hashVal = DatumGetInt32(FunctionCall1(&state->hashFn[attno], value));
+ 	srand(hashVal ^ rand());
+ 
+ 	for (j = 0; j < state->opts->bitSize[attno]; j++)
+ 	{
+ 		/* prevent mutiple evaluation */
+ 		nBit = rand() % (state->opts->bloomLength * BITSIGNTYPE); 
+ 		SETBIT(sign, nBit);
+ 	}
+ }
+ 
+ /*
+  * Make bloom tuple from values.
+  */
+ BloomTuple *
+ BloomFormTuple(BloomState *state, ItemPointer iptr, Datum *values, bool *isnull)
+ {
+ 	int 		i;
+ 	BloomTuple *res = (BloomTuple *)palloc0(state->sizeOfBloomTuple);
+ 
+ 	res->heapPtr = *iptr;
+ 
+     /*
+ 	 * Blooming
+ 	 */
+ 	for (i = 0; i < state->nColumns; i++)
+ 	{
+ 		/* skip nulls */
+ 		if (isnull[i])
+ 			continue;
+ 
+ 		signValue(state, res->sign, values[i], i);
+ 	}
+ 
+ 	return res;
+ }
+ 
+ /*
+  * Add new bloom tuple to the page.
+  */
+ bool
+ BloomPageAddItem(BloomState *state, Page page, BloomTuple *tuple)
+ {
+ 	BloomTuple		   *itup;
+ 	BloomPageOpaque		opaque;
+ 	Pointer				ptr;
+ 
+ 	if (BloomPageGetFreeSpace(state, page) < state->sizeOfBloomTuple)
+ 		return false;
+ 
+ 	/* Copy new tuple to the end of page */
+ 	opaque = BloomPageGetOpaque(page);
+ 	itup = BloomPageGetTuple(state, page, opaque->maxoff + 1);
+ 	memcpy((Pointer)itup, (Pointer)tuple, state->sizeOfBloomTuple);
+ 
+ 	/* Adjust maxoff and pd_lower */
+ 	opaque->maxoff++;
+ 	ptr = (Pointer)BloomPageGetTuple(state, page, opaque->maxoff + 1);
+ 	((PageHeader) page)->pd_lower = ptr - page;
+ 
+ 	return true;
+ }
+ 
+ /*
+  * Allocate a new page (either by recycling, or by extending the index file)
+  * The returned buffer is already pinned and exclusive-locked
+  * Caller is responsible for initializing the page by calling BloomInitBuffer
+  */
+ Buffer
+ BloomNewBuffer(Relation index)
+ {
+ 	Buffer      buffer;
+ 	bool        needLock;
+ 
+ 	/* First, try to get a page from FSM */
+ 	for (;;)
+ 	{
+ 		BlockNumber blkno = GetFreeIndexPage(index);
+ 
+ 		if (blkno == InvalidBlockNumber)
+ 			break;
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 
+ 		/*
+ 		 * We have to guard against the possibility that someone else already
+ 		 * recycled this page; the buffer may be locked if so.
+ 		 */
+ 		if (ConditionalLockBuffer(buffer))
+ 		{
+ 			Page	page = BufferGetPage(buffer);
+ 
+ 			if (PageIsNew(page))
+ 				return buffer;  /* OK to use, if never initialized */
+ 
+ 			if (BloomPageIsDeleted(page))
+ 				return buffer;  /* OK to use */
+ 
+ 			LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ 		}
+ 
+ 		/* Can't use it, so release buffer and try again */
+ 		ReleaseBuffer(buffer);
+ 	}
+ 
+ 	/* Must extend the file */
+ 	needLock = !RELATION_IS_LOCAL(index);
+ 	if (needLock)
+ 		LockRelationForExtension(index, ExclusiveLock);
+ 
+ 	buffer = ReadBuffer(index, P_NEW);
+ 	LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 	if (needLock)
+ 		UnlockRelationForExtension(index, ExclusiveLock);
+ 
+ 	return buffer;
+ }
+ 
+ /*
+  * Initialize bloom page.
+  */
+ void
+ BloomInitPage(Page page, uint16 flags)
+ {
+ 	BloomPageOpaque opaque;
+ 
+ 	PageInit(page, BLCKSZ, sizeof(BloomPageOpaqueData));
+ 
+ 	opaque = BloomPageGetOpaque(page);
+ 	memset(opaque, 0, sizeof(BloomPageOpaqueData));
+ 	opaque->maxoff = 0;
+ 	opaque->flags = flags;
+ }
+ 
+ static BloomOptions *
+ makeDefaultBloomOptions(BloomOptions *opts)
+ {
+ 	int i;
+ 
+ 	if (!opts)
+ 		opts = palloc0(sizeof(BloomOptions));
+ 
+ 	if (opts->bloomLength <= 0)
+ 		opts->bloomLength = 5;
+ 
+ 	for(i = 0; i < INDEX_MAX_KEYS; i++)
+ 		if (opts->bitSize[i] <= 0
+ 				|| opts->bitSize[i] >= opts->bloomLength * sizeof(SignType))
+ 			opts->bitSize[i] = 2;
+ 
+ 	return opts;
+ }
+ 
+ /*
+  * Initialize metapage for bloom index.
+  */
+ void
+ BloomInitMetapage(Relation index)
+ {
+ 	Page				metaPage;
+ 	Buffer				metaBuffer;
+ 	BloomMetaPageData  *metadata;
+ 
+ 	metaBuffer = BloomNewBuffer(index);
+ 	Assert(BufferGetBlockNumber(metaBuffer) == BLOOM_METAPAGE_BLKNO);
+ 
+ 	GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(metaBuffer, true);
+ 
+ 	BloomInitPage(metaPage, BLOOM_META);
+ 	metadata = BloomPageGetMeta(metaPage);
+ 	memset(metadata, 0, sizeof(BloomMetaPageData));
+ 	metadata->magickNumber = BLOOM_MAGICK_NUMBER;
+ 	metadata->opts = *makeDefaultBloomOptions((BloomOptions*)index->rd_options);
+ 	((PageHeader) metaPage)->pd_lower += sizeof(BloomMetaPageData);
+ 
+ 	GenericXLogFinish();
+ 	UnlockReleaseBuffer(metaBuffer);
+ }
+ 
+ /*
+  * Initialize options for bloom index.
+  */
+ bytea *
+ bloptions(Datum reloptions, bool validate)
+ {
+ 	relopt_value	   *options;
+ 	int					numoptions;
+ 	BloomOptions	   *rdopts;
+ 	relopt_parse_elt	tab[INDEX_MAX_KEYS + 1];
+ 	int					i;
+ 	char				buf[16];
+ 
+ 	tab[0].optname = "length";
+ 	tab[0].opttype = RELOPT_TYPE_INT;
+ 	tab[0].offset = offsetof(BloomOptions, bloomLength);
+ 
+ 	for(i = 0; i < INDEX_MAX_KEYS; i++)
+ 	{
+ 		snprintf(buf, sizeof(buf), "col%d", i + 1);
+ 		tab[i + 1].optname = pstrdup(buf);
+ 		tab[i + 1].opttype = RELOPT_TYPE_INT;
+ 		tab[i + 1].offset = offsetof(BloomOptions, bitSize[i]);
+ 	}
+ 
+ 	options = parseRelOptions(reloptions, validate, bl_relopt_kind, &numoptions);
+ 	rdopts = allocateReloptStruct(sizeof(BloomOptions), options, numoptions);
+ 	fillRelOptions((void *) rdopts, sizeof(BloomOptions), options, numoptions,
+ 						validate, tab, INDEX_MAX_KEYS + 1);
+ 		
+ 	rdopts = makeDefaultBloomOptions(rdopts);
+ 
+ 	return (bytea *)rdopts;
+ }
diff --git a/contrib/bloom/blvacuum.c b/contrib/bloom/blvacuum.c
new file mode 100644
index ...c694714
*** a/contrib/bloom/blvacuum.c
--- b/contrib/bloom/blvacuum.c
***************
*** 0 ****
--- 1,195 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blvacuum.c
+  *		Bloom VACUUM functions.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blvacuum.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "catalog/storage.h"
+ #include "commands/vacuum.h"
+ #include "miscadmin.h"
+ #include "postmaster/autovacuum.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "storage/lmgr.h"
+ 
+ #include "bloom.h"
+ 
+ IndexBulkDeleteResult *
+ blbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
+ 			 IndexBulkDeleteCallback callback, void *callback_state)
+ {
+ 	Relation				index = info->index;
+ 	BlockNumber				blkno,
+ 							npages;
+ 	FreeBlockNumberArray	notFullPage;
+ 	int						countPage = 0;
+ 	BloomState				state;
+ 	Buffer					buffer;
+ 	Page					page;
+ 
+ 	if (stats == NULL)
+ 		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+ 
+ 	initBloomState(&state, index); 
+ 
+ 	/*
+ 	 * Interate over the pages. We don't care about concurrently added pages,
+ 	 * they can't contain tuples to delete.
+ 	 */
+ 	npages = RelationGetNumberOfBlocks(index);
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		BloomTuple *itup, *itupPtr, *itupEnd;
+ 
+ 		buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
+ 									RBM_NORMAL, info->strategy);
+ 
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		GenericXLogStart(index);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		if (BloomPageIsDeleted(page))
+ 		{
+ 			UnlockReleaseBuffer(buffer);
+ 			CHECK_FOR_INTERRUPTS();
+ 			continue;
+ 		}
+ 
+ 		/* Iterate over the tuples */
+ 		itup = BloomPageGetTuple(&state, page, 1);
+ 		itupPtr = BloomPageGetTuple(&state, page, 1);
+ 		itupEnd = BloomPageGetTuple(&state, page, BloomPageGetMaxOffset(page) + 1);
+ 		while (itup < itupEnd)
+ 		{
+ 			/* Do we have to delete this tuple? */
+ 			if (callback(&itup->heapPtr, callback_state))
+ 			{
+ 				stats->tuples_removed += 1;
+ 				BloomPageGetOpaque(page)->maxoff--;
+ 			} 
+ 			else 
+ 			{
+ 				if (itupPtr != itup)
+ 				{
+ 					/*
+ 					 * If we already delete something before, we have to move
+ 					 * this tuple backward.
+ 					 */
+ 					memmove((Pointer)itupPtr, (Pointer)itup,
+ 							state.sizeOfBloomTuple);
+ 				}
+ 				stats->num_index_tuples++;
+ 				itupPtr = BloomPageGetNextTuple(&state, itupPtr);
+ 			}
+ 
+ 			itup = BloomPageGetNextTuple(&state, itup);
+ 		}
+ 
+ 		/* Did we delete something? */
+ 		if (itupPtr != itup)
+ 		{
+ 			/* Is it empty page now? */
+ 			if (itupPtr == BloomPageGetData(page))
+ 				BloomPageSetDeleted(page);
+ 			/* Adjust pg_lower */
+ 			((PageHeader) page)->pd_lower = (Pointer)itupPtr - page;
+ 			/* Finish WAL-logging */
+ 			GenericXLogFinish();
+ 		}
+ 		else
+ 		{
+ 			/* Didn't change anything: abort WAL-logging */
+ 			GenericXLogAbort();
+ 		}
+ 
+ 		if (!BloomPageIsDeleted(page) && 
+ 				BloomPageGetFreeSpace(&state, page) > state.sizeOfBloomTuple && 
+ 				countPage < BloomMetaBlockN)
+ 			notFullPage[countPage++] = blkno;
+ 		UnlockReleaseBuffer(buffer);
+ 		CHECK_FOR_INTERRUPTS();
+ 	}
+ 
+ 	if (countPage > 0)
+ 	{
+ 		BloomMetaPageData	*metaData;
+ 
+ 		buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 		GenericXLogStart(index);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		metaData = BloomPageGetMeta(page);
+ 		memcpy(metaData->notFullPage, notFullPage, sizeof(FreeBlockNumberArray));
+ 		metaData->nStart=0;
+ 		metaData->nEnd = countPage;
+ 
+ 		GenericXLogFinish();
+ 		UnlockReleaseBuffer(buffer);
+ 	}
+ 
+ 	return stats;
+ }
+ 
+ IndexBulkDeleteResult *
+ blvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
+ {
+ 	Relation	index = info->index;
+ 	BlockNumber npages,
+ 				blkno;
+ 	BlockNumber	totFreePages;
+ 
+ 	if (info->analyze_only)
+ 		return stats;
+ 
+ 	if (stats == NULL)
+ 		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+ 
+ 	/*
+ 	 * Iterate over the pages: insert deleted pages into FSM and collect
+ 	 * statistics.
+ 	 */
+ 	npages = RelationGetNumberOfBlocks(index);
+ 	totFreePages = 0;
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		Buffer		buffer;
+ 		Page		page;
+ 
+ 		vacuum_delay_point();
+ 
+ 		buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
+ 									RBM_NORMAL, info->strategy);
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 		page = (Page) BufferGetPage(buffer);
+ 
+ 		if (BloomPageIsDeleted(page))
+ 		{
+ 			RecordFreeIndexPage(index, blkno);
+ 			totFreePages++;
+ 		}
+ 		else
+ 		{
+ 			stats->num_index_tuples += BloomPageGetMaxOffset(page);
+ 			stats->estimated_count += BloomPageGetMaxOffset(page);
+ 		}
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 	}
+ 
+ 	IndexFreeSpaceMapVacuum(info->index);
+ 	stats->pages_free = totFreePages;
+ 	stats->num_pages = RelationGetNumberOfBlocks(index);
+ 
+ 	return stats;
+ }
diff --git a/contrib/bloom/blvalidate.c b/contrib/bloom/blvalidate.c
new file mode 100644
index ...5344b81
*** a/contrib/bloom/blvalidate.c
--- b/contrib/bloom/blvalidate.c
***************
*** 0 ****
--- 1,220 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blvalidate.c
+  *	  Opclass validator for bloom.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blvalidate.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/amvalidate.h"
+ #include "access/htup_details.h"
+ #include "catalog/pg_amop.h"
+ #include "catalog/pg_amproc.h"
+ #include "catalog/pg_opclass.h"
+ #include "catalog/pg_opfamily.h"
+ #include "catalog/pg_type.h"
+ #include "utils/builtins.h"
+ #include "utils/lsyscache.h"
+ #include "utils/syscache.h"
+ 
+ #include "bloom.h"
+ 
+ /*
+  * Validator for a bloom opclass.
+  */
+ bool
+ blvalidate(Oid opclassoid)
+ {
+ 	bool		result = true;
+ 	HeapTuple	classtup;
+ 	Form_pg_opclass classform;
+ 	Oid			opfamilyoid;
+ 	Oid			opcintype;
+ 	Oid			opckeytype;
+ 	char	   *opclassname;
+ 	HeapTuple	familytup;
+ 	Form_pg_opfamily familyform;
+ 	char	   *opfamilyname;
+ 	CatCList   *proclist,
+ 			   *oprlist;
+ 	List	   *grouplist;
+ 	OpFamilyOpFuncGroup *opclassgroup;
+ 	int			i;
+ 	ListCell   *lc;
+ 
+ 	/* Fetch opclass information */
+ 	classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
+ 	if (!HeapTupleIsValid(classtup))
+ 		elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
+ 	classform = (Form_pg_opclass) GETSTRUCT(classtup);
+ 
+ 	opfamilyoid = classform->opcfamily;
+ 	opcintype = classform->opcintype;
+ 	opckeytype = classform->opckeytype;
+ 	if (!OidIsValid(opckeytype))
+ 		opckeytype = opcintype;
+ 	opclassname = NameStr(classform->opcname);
+ 
+ 	/* Fetch opfamily information */
+ 	familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
+ 	if (!HeapTupleIsValid(familytup))
+ 		elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
+ 	familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
+ 
+ 	opfamilyname = NameStr(familyform->opfname);
+ 
+ 	/* Fetch all operators and support functions of the opfamily */
+ 	oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
+ 	proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
+ 
+ 	/* Check individual support functions */
+ 	for (i = 0; i < proclist->n_members; i++)
+ 	{
+ 		HeapTuple	proctup = &proclist->members[i]->tuple;
+ 		Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
+ 		bool		ok;
+ 
+ 		/*
+ 		 * All bloom support functions should be registered with matching
+ 		 * left/right types
+ 		 */
+ 		if (procform->amproclefttype != procform->amprocrighttype)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains support procedure %s with cross-type registration",
+ 							opfamilyname,
+ 							format_procedure(procform->amproc))));
+ 			result = false;
+ 		}
+ 
+ 		/*
+ 		 * We can't check signatures except within the specific opclass, since
+ 		 * we need to know the associated opckeytype in many cases.
+ 		 */
+ 		if (procform->amproclefttype != opcintype)
+ 			continue;
+ 
+ 		/* Check procedure numbers and function signatures */
+ 		switch (procform->amprocnum)
+ 		{
+ 			case BLOOM_HASH_PROC:
+ 				ok = check_amproc_signature(procform->amproc, INT4OID, false,
+ 											1, 1, opckeytype);
+ 				break;
+ 			default:
+ 				ereport(INFO,
+ 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 						 errmsg("bloom opfamily %s contains function %s with invalid support number %d",
+ 								opfamilyname,
+ 								format_procedure(procform->amproc),
+ 								procform->amprocnum)));
+ 				result = false;
+ 				continue;		/* don't want additional message */
+ 		}
+ 
+ 		if (!ok)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("gist opfamily %s contains function %s with wrong signature for support number %d",
+ 							opfamilyname,
+ 							format_procedure(procform->amproc),
+ 							procform->amprocnum)));
+ 			result = false;
+ 		}
+ 	}
+ 
+ 	/* Check individual operators */
+ 	for (i = 0; i < oprlist->n_members; i++)
+ 	{
+ 		HeapTuple	oprtup = &oprlist->members[i]->tuple;
+ 		Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
+ 
+ 		/* Check it's allowed strategy for bloom */
+ 		if (oprform->amopstrategy < 1 || 
+ 			oprform->amopstrategy > BLOOM_NSTRATEGIES)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains operator %s with invalid strategy number %d",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr),
+ 							oprform->amopstrategy)));
+ 			result = false;
+ 		}
+ 
+ 		/* bloom doesn't support ORDER BY operators */
+ 		if (oprform->amoppurpose != AMOP_SEARCH ||
+ 			OidIsValid(oprform->amopsortfamily))
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains invalid ORDER BY specification for operator %s",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr))));
+ 			result = false;
+ 		}
+ 
+ 		/* Check operator signature --- same for all bloom strategies */
+ 		if (!check_amop_signature(oprform->amopopr, BOOLOID,
+ 								  oprform->amoplefttype,
+ 								  oprform->amoprighttype))
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains operator %s with wrong signature",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr))));
+ 			result = false;
+ 		}
+ 	}
+ 
+ 	/* Now check for inconsistent groups of operators/functions */
+ 	grouplist = identify_opfamily_groups(oprlist, proclist);
+ 	opclassgroup = NULL;
+ 	foreach(lc, grouplist)
+ 	{
+ 		OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
+ 
+ 		/* Remember the group exactly matching the test opclass */
+ 		if (thisgroup->lefttype == opcintype &&
+ 			thisgroup->righttype == opcintype)
+ 			opclassgroup = thisgroup;
+ 
+ 		/*
+ 		 * There is not a lot we can do to check the operator sets, since each
+ 		 * bloom opclass is more or less a law unto itself, and some contain
+ 		 * only operators that are binary-compatible with the opclass datatype
+ 		 * (meaning that empty operator sets can be OK).  That case also means
+ 		 * that we shouldn't insist on nonempty function sets except for the
+ 		 * opclass's own group.
+ 		 */
+ 	}
+ 
+ 	/* Check that the originally-named opclass is complete */
+ 	for (i = 1; i <= BLOOM_NPROC; i++)
+ 	{
+ 		if (opclassgroup &&
+ 			(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
+ 			continue;			/* got it */
+ 		ereport(INFO,
+ 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 				 errmsg("bloom opclass %s is missing support function %d",
+ 						opclassname, i)));
+ 		result = false;
+ 	}
+ 
+ 	ReleaseCatCacheList(proclist);
+ 	ReleaseCatCacheList(oprlist);
+ 	ReleaseSysCache(familytup);
+ 	ReleaseSysCache(classtup);
+ 
+ 	return result;
+ }
diff --git a/contrib/bloom/data/data b/contrib/bloom/data/data
new file mode 100644
index ...eacf3e7
*** a/contrib/bloom/data/data
--- b/contrib/bloom/data/data
***************
*** 0 ****
--- 1,10000 ----
+ 739	3
+ 475	9
+ 45	6
+ 433	1
+ 948	8
+ 926	8
+ 397	7
+ 980	4
+ 212	5
+ 522	9
+ 74	8
+ 77	4
+ 378	9
+ 575	3
+ 625	2
+ 407	4
+ 509	9
+ 252	6
+ 487	7
+ 656	4
+ 485	6
+ 275	9
+ 285	3
+ 277	5
+ 804	8
+ 424	9
+ 553	5
+ 245	9
+ 384	8
+ 202	0
+ 43	6
+ 374	6
+ 490	6
+ 105	10
+ 311	8
+ 411	8
+ 343	7
+ 678	6
+ 942	10
+ 126	7
+ 755	6
+ 625	3
+ 52	9
+ 239	4
+ 690	4
+ 445	7
+ 49	8
+ 285	5
+ 445	4
+ 516	8
+ 151	9
+ 553	5
+ 613	2
+ 123	6
+ 187	2
+ 301	9
+ 800	9
+ 250	9
+ 796	5
+ 288	5
+ 930	7
+ 219	10
+ 553	5
+ 518	10
+ 893	0
+ 754	0
+ 960	3
+ 538	6
+ 537	7
+ 127	7
+ 910	4
+ 666	7
+ 354	9
+ 562	1
+ 405	8
+ 635	3
+ 583	9
+ 313	1
+ 358	8
+ 133	3
+ 864	9
+ 296	8
+ 193	8
+ 396	7
+ 495	5
+ 454	4
+ 952	1
+ 115	3
+ 36	7
+ 455	4
+ 527	1
+ 775	1
+ 945	1
+ 246	3
+ 920	4
+ 554	8
+ 267	8
+ 608	5
+ 684	0
+ 190	2
+ 527	6
+ 584	5
+ 764	7
+ 785	8
+ 376	2
+ 240	9
+ 330	0
+ 13	3
+ 103	3
+ 578	0
+ 639	1
+ 807	9
+ 982	4
+ 365	7
+ 418	6
+ 844	9
+ 199	4
+ 425	10
+ 127	2
+ 762	5
+ 450	0
+ 406	8
+ 17	4
+ 55	1
+ 678	6
+ 143	3
+ 764	9
+ 222	7
+ 364	6
+ 411	8
+ 142	3
+ 728	3
+ 684	2
+ 303	8
+ 363	1
+ 314	8
+ 67	7
+ 592	1
+ 139	6
+ 204	8
+ 279	3
+ 133	0
+ 296	4
+ 788	7
+ 942	2
+ 441	1
+ 454	2
+ 424	1
+ 322	7
+ 949	7
+ 793	3
+ 497	9
+ 984	1
+ 944	1
+ 736	1
+ 940	0
+ 494	1
+ 57	8
+ 429	8
+ 449	4
+ 44	9
+ 454	5
+ 60	9
+ 636	4
+ 606	6
+ 67	4
+ 848	6
+ 259	8
+ 654	2
+ 955	4
+ 351	9
+ 405	8
+ 968	5
+ 634	4
+ 308	1
+ 767	4
+ 974	2
+ 850	0
+ 99	5
+ 416	7
+ 71	5
+ 103	9
+ 48	4
+ 750	7
+ 565	7
+ 92	9
+ 599	5
+ 760	6
+ 960	4
+ 964	3
+ 478	7
+ 620	5
+ 953	5
+ 485	1
+ 957	9
+ 756	0
+ 383	9
+ 946	4
+ 222	7
+ 133	8
+ 401	2
+ 702	0
+ 723	5
+ 568	7
+ 857	5
+ 951	3
+ 264	6
+ 786	2
+ 42	3
+ 268	10
+ 172	0
+ 26	6
+ 884	10
+ 986	1
+ 667	1
+ 893	1
+ 344	6
+ 68	1
+ 58	6
+ 750	9
+ 168	7
+ 249	4
+ 273	0
+ 649	3
+ 306	9
+ 314	5
+ 942	3
+ 33	8
+ 311	0
+ 932	10
+ 138	8
+ 47	5
+ 420	1
+ 550	5
+ 751	3
+ 392	9
+ 1	6
+ 351	3
+ 677	10
+ 588	10
+ 917	9
+ 461	9
+ 242	5
+ 685	6
+ 514	6
+ 531	7
+ 442	6
+ 135	9
+ 693	7
+ 341	4
+ 984	7
+ 362	10
+ 375	7
+ 259	1
+ 713	8
+ 35	6
+ 750	5
+ 489	10
+ 991	2
+ 544	5
+ 791	1
+ 156	2
+ 653	3
+ 96	3
+ 976	4
+ 789	10
+ 170	2
+ 946	5
+ 865	2
+ 597	6
+ 53	6
+ 209	8
+ 128	7
+ 794	1
+ 871	3
+ 623	7
+ 413	8
+ 895	1
+ 70	10
+ 411	0
+ 428	2
+ 6	6
+ 352	10
+ 143	2
+ 156	7
+ 795	2
+ 372	0
+ 11	5
+ 701	8
+ 619	6
+ 142	2
+ 233	6
+ 20	1
+ 621	1
+ 118	0
+ 136	5
+ 232	1
+ 145	6
+ 93	3
+ 800	2
+ 28	6
+ 457	4
+ 598	5
+ 900	3
+ 273	5
+ 870	4
+ 760	1
+ 970	8
+ 231	6
+ 871	3
+ 622	0
+ 895	9
+ 148	0
+ 439	2
+ 328	2
+ 489	4
+ 834	9
+ 756	4
+ 415	7
+ 730	7
+ 175	6
+ 102	9
+ 703	1
+ 715	9
+ 662	6
+ 283	3
+ 592	2
+ 139	7
+ 218	6
+ 981	5
+ 816	5
+ 902	7
+ 417	7
+ 82	8
+ 314	8
+ 519	5
+ 413	6
+ 424	1
+ 693	1
+ 50	4
+ 725	3
+ 639	3
+ 511	8
+ 58	7
+ 356	0
+ 275	2
+ 510	2
+ 822	9
+ 835	9
+ 759	1
+ 717	3
+ 639	1
+ 899	1
+ 246	6
+ 202	3
+ 946	9
+ 629	6
+ 245	1
+ 363	3
+ 870	7
+ 342	1
+ 891	9
+ 322	7
+ 778	2
+ 617	5
+ 307	3
+ 814	9
+ 463	7
+ 8	7
+ 304	2
+ 5	2
+ 138	6
+ 835	4
+ 774	2
+ 685	6
+ 916	0
+ 789	8
+ 878	1
+ 519	7
+ 269	1
+ 193	6
+ 470	0
+ 522	9
+ 720	5
+ 643	0
+ 741	6
+ 275	9
+ 282	1
+ 261	1
+ 306	9
+ 701	2
+ 973	5
+ 28	9
+ 602	5
+ 507	9
+ 683	7
+ 448	2
+ 708	10
+ 88	4
+ 501	7
+ 453	2
+ 379	7
+ 120	7
+ 836	4
+ 717	1
+ 327	4
+ 365	3
+ 908	4
+ 151	5
+ 940	7
+ 381	6
+ 359	8
+ 777	1
+ 799	9
+ 495	3
+ 595	9
+ 542	10
+ 675	7
+ 634	5
+ 44	4
+ 654	4
+ 769	0
+ 671	7
+ 411	8
+ 187	4
+ 481	6
+ 974	8
+ 398	8
+ 907	2
+ 615	4
+ 497	2
+ 349	0
+ 183	0
+ 701	8
+ 535	7
+ 169	2
+ 116	9
+ 208	8
+ 615	6
+ 610	8
+ 970	1
+ 371	9
+ 931	8
+ 695	8
+ 966	3
+ 239	5
+ 520	6
+ 502	7
+ 612	2
+ 520	1
+ 948	7
+ 337	1
+ 627	5
+ 852	2
+ 165	5
+ 45	1
+ 554	4
+ 79	5
+ 185	8
+ 323	2
+ 84	6
+ 613	6
+ 151	1
+ 306	8
+ 318	8
+ 911	3
+ 516	2
+ 331	1
+ 793	2
+ 385	10
+ 646	4
+ 92	2
+ 846	2
+ 686	0
+ 945	0
+ 181	0
+ 572	8
+ 633	7
+ 909	9
+ 486	2
+ 766	4
+ 493	3
+ 645	8
+ 424	4
+ 8	8
+ 395	7
+ 240	5
+ 855	1
+ 659	5
+ 117	6
+ 551	3
+ 634	1
+ 93	3
+ 846	0
+ 206	3
+ 228	10
+ 730	7
+ 253	4
+ 546	7
+ 813	6
+ 487	2
+ 209	7
+ 696	1
+ 814	4
+ 605	9
+ 959	2
+ 230	6
+ 278	3
+ 860	1
+ 324	1
+ 457	6
+ 37	2
+ 273	3
+ 561	8
+ 968	4
+ 373	5
+ 582	6
+ 183	3
+ 646	10
+ 633	3
+ 928	6
+ 407	2
+ 185	7
+ 480	0
+ 810	8
+ 111	3
+ 355	1
+ 453	6
+ 439	0
+ 447	4
+ 388	8
+ 862	10
+ 402	0
+ 247	0
+ 42	9
+ 299	10
+ 472	7
+ 127	7
+ 392	6
+ 702	2
+ 410	8
+ 468	8
+ 960	9
+ 394	4
+ 935	8
+ 806	3
+ 661	7
+ 292	1
+ 714	5
+ 111	8
+ 419	4
+ 725	9
+ 117	9
+ 548	5
+ 460	2
+ 711	9
+ 62	2
+ 636	0
+ 99	0
+ 421	0
+ 870	2
+ 357	5
+ 896	6
+ 594	6
+ 189	7
+ 365	6
+ 116	1
+ 499	2
+ 943	0
+ 742	4
+ 297	5
+ 273	4
+ 631	9
+ 382	7
+ 938	8
+ 765	8
+ 30	1
+ 338	9
+ 771	9
+ 535	10
+ 637	9
+ 568	8
+ 990	1
+ 986	9
+ 114	7
+ 335	4
+ 181	6
+ 770	8
+ 517	2
+ 543	5
+ 954	3
+ 262	10
+ 430	6
+ 910	2
+ 531	4
+ 160	2
+ 345	7
+ 921	3
+ 795	9
+ 267	9
+ 635	6
+ 319	8
+ 211	1
+ 628	7
+ 240	2
+ 182	2
+ 479	4
+ 179	9
+ 43	1
+ 110	6
+ 535	3
+ 743	9
+ 999	7
+ 214	8
+ 571	5
+ 702	2
+ 85	0
+ 22	3
+ 111	6
+ 23	4
+ 821	2
+ 546	3
+ 648	7
+ 210	7
+ 814	3
+ 267	3
+ 590	0
+ 228	6
+ 674	4
+ 382	2
+ 924	1
+ 450	0
+ 106	5
+ 304	2
+ 121	3
+ 568	9
+ 532	1
+ 243	2
+ 839	5
+ 872	7
+ 772	1
+ 2	4
+ 148	2
+ 950	8
+ 673	3
+ 66	6
+ 416	5
+ 606	5
+ 987	9
+ 739	1
+ 237	3
+ 51	8
+ 422	3
+ 949	3
+ 746	8
+ 914	5
+ 959	9
+ 880	1
+ 146	8
+ 929	8
+ 163	10
+ 416	6
+ 511	0
+ 102	5
+ 932	8
+ 606	2
+ 149	7
+ 939	6
+ 951	9
+ 831	7
+ 710	7
+ 215	7
+ 662	1
+ 777	8
+ 926	7
+ 627	1
+ 701	0
+ 668	2
+ 66	8
+ 709	10
+ 611	3
+ 168	8
+ 973	1
+ 330	9
+ 996	2
+ 620	7
+ 907	8
+ 375	6
+ 931	2
+ 377	9
+ 857	0
+ 945	6
+ 49	6
+ 769	1
+ 384	5
+ 113	10
+ 794	3
+ 756	8
+ 389	1
+ 690	4
+ 248	3
+ 90	2
+ 146	5
+ 724	1
+ 616	1
+ 933	5
+ 106	9
+ 31	2
+ 492	8
+ 270	9
+ 279	4
+ 872	1
+ 664	6
+ 840	1
+ 713	5
+ 438	10
+ 841	5
+ 116	10
+ 994	8
+ 63	6
+ 942	10
+ 83	0
+ 874	1
+ 203	4
+ 914	5
+ 242	2
+ 856	1
+ 265	5
+ 742	1
+ 573	5
+ 635	0
+ 416	5
+ 540	5
+ 462	5
+ 373	5
+ 143	3
+ 520	2
+ 363	4
+ 340	6
+ 760	3
+ 40	0
+ 446	9
+ 117	7
+ 416	9
+ 816	10
+ 313	5
+ 0	7
+ 927	5
+ 261	4
+ 74	6
+ 914	2
+ 949	4
+ 443	3
+ 829	8
+ 879	6
+ 37	9
+ 591	5
+ 814	7
+ 195	2
+ 566	0
+ 218	9
+ 462	2
+ 608	4
+ 759	9
+ 778	8
+ 504	7
+ 49	5
+ 127	5
+ 765	10
+ 276	6
+ 544	3
+ 562	1
+ 797	4
+ 843	10
+ 605	4
+ 2	8
+ 288	5
+ 42	9
+ 853	8
+ 765	6
+ 633	3
+ 324	7
+ 722	5
+ 175	5
+ 406	5
+ 130	9
+ 765	7
+ 85	6
+ 68	9
+ 553	7
+ 337	6
+ 497	6
+ 19	5
+ 520	9
+ 340	3
+ 504	10
+ 554	8
+ 656	3
+ 279	8
+ 763	7
+ 283	9
+ 634	0
+ 585	7
+ 609	7
+ 647	2
+ 326	10
+ 717	8
+ 608	7
+ 362	1
+ 608	7
+ 413	1
+ 676	10
+ 940	3
+ 244	2
+ 163	0
+ 903	4
+ 899	5
+ 494	5
+ 256	1
+ 136	9
+ 264	5
+ 886	10
+ 285	5
+ 717	6
+ 621	3
+ 349	0
+ 436	0
+ 2	4
+ 356	2
+ 595	5
+ 251	5
+ 965	2
+ 34	5
+ 633	3
+ 562	8
+ 192	8
+ 231	1
+ 807	5
+ 571	5
+ 163	2
+ 848	5
+ 226	3
+ 536	2
+ 661	9
+ 473	3
+ 412	7
+ 753	4
+ 874	8
+ 837	5
+ 77	4
+ 277	3
+ 225	5
+ 347	0
+ 24	9
+ 555	2
+ 109	4
+ 699	3
+ 688	2
+ 563	3
+ 128	0
+ 604	5
+ 759	4
+ 919	6
+ 143	8
+ 141	2
+ 154	4
+ 488	4
+ 926	8
+ 410	10
+ 752	10
+ 137	9
+ 369	8
+ 197	1
+ 72	8
+ 405	2
+ 795	0
+ 741	6
+ 365	7
+ 187	5
+ 415	3
+ 728	6
+ 745	2
+ 948	7
+ 51	4
+ 621	8
+ 324	8
+ 665	7
+ 595	9
+ 750	7
+ 622	2
+ 867	4
+ 164	6
+ 971	5
+ 267	2
+ 38	7
+ 485	8
+ 251	2
+ 982	2
+ 902	0
+ 556	5
+ 836	9
+ 282	5
+ 573	9
+ 364	3
+ 543	10
+ 477	4
+ 403	6
+ 18	4
+ 171	3
+ 531	2
+ 966	0
+ 974	2
+ 247	10
+ 415	1
+ 988	10
+ 672	8
+ 851	10
+ 325	4
+ 830	7
+ 746	4
+ 675	2
+ 784	1
+ 865	8
+ 450	0
+ 86	10
+ 244	1
+ 998	2
+ 269	2
+ 173	7
+ 394	2
+ 655	1
+ 986	5
+ 20	3
+ 930	8
+ 0	7
+ 224	7
+ 900	0
+ 752	8
+ 809	2
+ 800	9
+ 184	0
+ 947	2
+ 261	2
+ 427	4
+ 899	8
+ 596	6
+ 887	6
+ 60	9
+ 894	10
+ 757	9
+ 667	10
+ 569	6
+ 987	3
+ 331	8
+ 524	1
+ 691	7
+ 174	6
+ 891	4
+ 854	3
+ 870	8
+ 139	5
+ 307	0
+ 48	4
+ 933	9
+ 358	7
+ 836	0
+ 670	4
+ 591	7
+ 726	9
+ 454	3
+ 53	1
+ 959	2
+ 783	8
+ 663	6
+ 168	5
+ 389	3
+ 999	7
+ 334	0
+ 64	3
+ 989	4
+ 957	8
+ 447	6
+ 231	0
+ 285	10
+ 960	7
+ 208	0
+ 883	2
+ 240	7
+ 16	9
+ 302	2
+ 435	7
+ 490	4
+ 388	8
+ 481	5
+ 91	5
+ 874	0
+ 296	3
+ 675	5
+ 359	10
+ 484	3
+ 698	7
+ 332	6
+ 858	6
+ 247	9
+ 475	5
+ 57	9
+ 241	5
+ 344	6
+ 371	8
+ 81	5
+ 296	10
+ 509	6
+ 277	2
+ 120	6
+ 143	6
+ 955	8
+ 296	3
+ 421	2
+ 860	7
+ 28	3
+ 217	1
+ 244	5
+ 632	6
+ 87	0
+ 414	2
+ 465	7
+ 123	10
+ 303	4
+ 158	4
+ 36	3
+ 27	10
+ 142	3
+ 278	6
+ 476	1
+ 231	5
+ 472	4
+ 588	7
+ 907	2
+ 305	10
+ 223	7
+ 161	7
+ 428	3
+ 662	7
+ 684	8
+ 154	7
+ 121	2
+ 711	3
+ 503	10
+ 826	10
+ 127	1
+ 483	6
+ 506	1
+ 316	4
+ 292	6
+ 407	5
+ 339	6
+ 203	8
+ 853	9
+ 499	5
+ 684	7
+ 257	8
+ 833	10
+ 68	3
+ 958	9
+ 316	1
+ 950	8
+ 685	5
+ 870	0
+ 869	2
+ 622	3
+ 676	10
+ 844	9
+ 729	7
+ 743	2
+ 234	4
+ 881	5
+ 233	7
+ 460	3
+ 51	4
+ 193	4
+ 503	1
+ 165	2
+ 600	0
+ 189	5
+ 197	8
+ 745	9
+ 773	6
+ 752	5
+ 285	5
+ 730	5
+ 923	6
+ 10	2
+ 324	5
+ 455	4
+ 888	6
+ 741	4
+ 792	9
+ 579	4
+ 942	8
+ 862	1
+ 580	6
+ 12	4
+ 196	8
+ 854	5
+ 259	6
+ 1	2
+ 195	0
+ 336	5
+ 481	8
+ 894	4
+ 440	6
+ 760	2
+ 542	3
+ 625	5
+ 107	5
+ 622	7
+ 94	6
+ 40	3
+ 397	9
+ 771	7
+ 479	8
+ 837	7
+ 783	2
+ 192	3
+ 964	1
+ 633	4
+ 721	4
+ 637	3
+ 732	3
+ 746	8
+ 749	4
+ 527	8
+ 2	6
+ 133	4
+ 462	9
+ 54	9
+ 677	9
+ 613	5
+ 64	8
+ 725	0
+ 891	4
+ 433	6
+ 751	1
+ 876	5
+ 332	6
+ 324	1
+ 990	9
+ 925	10
+ 418	1
+ 390	9
+ 962	4
+ 820	6
+ 335	4
+ 99	4
+ 239	8
+ 427	1
+ 182	9
+ 743	9
+ 930	6
+ 418	3
+ 241	7
+ 344	2
+ 593	3
+ 223	0
+ 326	6
+ 891	3
+ 58	7
+ 928	4
+ 145	0
+ 792	4
+ 851	2
+ 514	0
+ 80	3
+ 967	0
+ 875	4
+ 272	1
+ 126	6
+ 347	7
+ 884	6
+ 730	2
+ 184	6
+ 498	2
+ 333	4
+ 635	5
+ 453	4
+ 861	3
+ 647	4
+ 338	7
+ 632	3
+ 736	5
+ 689	0
+ 624	8
+ 623	10
+ 534	5
+ 541	3
+ 717	7
+ 885	2
+ 967	2
+ 641	6
+ 696	1
+ 29	6
+ 399	7
+ 933	7
+ 403	6
+ 41	1
+ 73	7
+ 147	7
+ 546	8
+ 668	1
+ 278	2
+ 344	10
+ 934	2
+ 209	9
+ 447	8
+ 503	1
+ 944	5
+ 700	3
+ 208	6
+ 79	6
+ 198	1
+ 750	3
+ 851	9
+ 967	4
+ 668	6
+ 477	9
+ 843	8
+ 940	8
+ 51	1
+ 678	5
+ 999	2
+ 641	9
+ 713	3
+ 285	9
+ 974	4
+ 532	2
+ 485	3
+ 442	3
+ 179	4
+ 733	8
+ 44	2
+ 792	9
+ 32	7
+ 664	1
+ 880	3
+ 581	9
+ 523	2
+ 822	2
+ 563	1
+ 157	5
+ 471	7
+ 709	10
+ 971	2
+ 292	2
+ 561	0
+ 997	6
+ 236	8
+ 491	3
+ 521	2
+ 351	4
+ 498	9
+ 281	0
+ 153	1
+ 258	7
+ 209	4
+ 253	7
+ 105	10
+ 636	1
+ 113	9
+ 227	7
+ 954	2
+ 278	2
+ 14	8
+ 459	5
+ 926	8
+ 937	4
+ 742	2
+ 446	9
+ 320	7
+ 611	5
+ 120	9
+ 210	2
+ 827	8
+ 301	9
+ 775	5
+ 614	7
+ 753	9
+ 918	8
+ 663	4
+ 302	6
+ 187	2
+ 13	9
+ 457	5
+ 824	8
+ 163	4
+ 307	3
+ 300	5
+ 508	1
+ 363	8
+ 67	1
+ 338	7
+ 866	1
+ 573	8
+ 858	2
+ 161	2
+ 824	3
+ 399	8
+ 277	9
+ 295	1
+ 633	5
+ 536	9
+ 742	8
+ 456	3
+ 963	8
+ 61	0
+ 956	4
+ 710	8
+ 490	3
+ 606	3
+ 519	8
+ 508	3
+ 116	9
+ 179	4
+ 762	5
+ 494	4
+ 934	0
+ 335	7
+ 867	8
+ 926	8
+ 610	10
+ 859	6
+ 386	6
+ 389	9
+ 852	10
+ 224	4
+ 763	7
+ 713	9
+ 638	9
+ 272	4
+ 367	8
+ 796	3
+ 796	1
+ 977	7
+ 921	9
+ 493	5
+ 890	4
+ 98	3
+ 921	5
+ 152	8
+ 482	4
+ 143	2
+ 108	9
+ 124	7
+ 750	4
+ 147	1
+ 162	9
+ 418	10
+ 73	4
+ 622	10
+ 298	1
+ 526	2
+ 466	6
+ 464	4
+ 111	6
+ 159	6
+ 992	3
+ 837	1
+ 159	10
+ 847	9
+ 357	10
+ 26	5
+ 937	4
+ 478	0
+ 839	1
+ 5	1
+ 214	5
+ 325	7
+ 156	8
+ 66	3
+ 405	2
+ 859	4
+ 527	7
+ 498	7
+ 658	3
+ 595	0
+ 339	6
+ 535	3
+ 65	0
+ 286	9
+ 112	3
+ 41	3
+ 823	4
+ 6	10
+ 154	1
+ 245	6
+ 295	1
+ 957	8
+ 800	5
+ 508	5
+ 801	1
+ 473	1
+ 723	0
+ 415	8
+ 21	7
+ 691	1
+ 993	7
+ 460	8
+ 97	5
+ 795	3
+ 536	0
+ 811	8
+ 144	8
+ 654	9
+ 224	2
+ 403	0
+ 263	9
+ 165	10
+ 884	6
+ 774	9
+ 282	5
+ 39	3
+ 197	5
+ 91	3
+ 964	9
+ 546	5
+ 926	4
+ 332	1
+ 127	10
+ 15	4
+ 146	4
+ 376	4
+ 293	5
+ 396	2
+ 120	2
+ 83	4
+ 636	1
+ 677	8
+ 620	8
+ 128	6
+ 655	7
+ 84	6
+ 32	4
+ 651	2
+ 400	7
+ 510	5
+ 83	9
+ 957	4
+ 426	4
+ 554	5
+ 523	6
+ 949	2
+ 758	6
+ 992	4
+ 395	1
+ 962	0
+ 794	0
+ 630	8
+ 461	3
+ 984	9
+ 947	5
+ 408	0
+ 380	4
+ 407	8
+ 717	10
+ 352	2
+ 598	3
+ 399	4
+ 927	4
+ 734	3
+ 510	7
+ 371	3
+ 742	0
+ 129	2
+ 283	1
+ 63	2
+ 608	5
+ 261	10
+ 835	7
+ 793	6
+ 628	1
+ 793	2
+ 446	2
+ 582	4
+ 583	3
+ 695	1
+ 13	1
+ 397	8
+ 68	5
+ 957	4
+ 641	0
+ 582	2
+ 491	8
+ 235	3
+ 510	0
+ 879	1
+ 173	7
+ 365	6
+ 863	9
+ 992	4
+ 264	7
+ 540	3
+ 754	9
+ 32	8
+ 464	10
+ 174	1
+ 9	8
+ 353	5
+ 598	6
+ 827	1
+ 616	7
+ 247	8
+ 377	6
+ 407	2
+ 558	4
+ 686	8
+ 86	2
+ 99	8
+ 163	1
+ 662	6
+ 120	8
+ 731	1
+ 591	1
+ 630	2
+ 671	5
+ 298	3
+ 162	5
+ 75	5
+ 155	5
+ 779	7
+ 880	5
+ 535	10
+ 691	6
+ 806	9
+ 764	5
+ 480	9
+ 303	2
+ 13	9
+ 294	6
+ 84	10
+ 100	4
+ 252	3
+ 926	3
+ 801	1
+ 808	6
+ 794	7
+ 45	3
+ 655	7
+ 963	5
+ 589	7
+ 929	1
+ 611	2
+ 279	6
+ 127	6
+ 267	2
+ 538	4
+ 592	8
+ 629	5
+ 117	4
+ 599	9
+ 10	4
+ 614	1
+ 722	3
+ 790	7
+ 730	4
+ 413	7
+ 447	0
+ 891	7
+ 648	0
+ 299	9
+ 228	8
+ 282	8
+ 627	9
+ 338	7
+ 340	9
+ 669	3
+ 330	3
+ 404	1
+ 552	2
+ 738	3
+ 574	2
+ 941	0
+ 174	8
+ 747	8
+ 849	0
+ 738	1
+ 884	0
+ 897	5
+ 931	2
+ 256	3
+ 173	9
+ 621	5
+ 209	0
+ 556	8
+ 220	3
+ 43	8
+ 444	10
+ 815	6
+ 816	6
+ 441	7
+ 609	2
+ 742	5
+ 199	6
+ 4	1
+ 875	3
+ 400	0
+ 185	0
+ 551	4
+ 46	1
+ 155	3
+ 400	2
+ 60	8
+ 183	9
+ 463	10
+ 436	9
+ 665	0
+ 82	4
+ 538	3
+ 47	5
+ 410	9
+ 802	8
+ 970	10
+ 832	5
+ 381	9
+ 627	5
+ 145	0
+ 734	2
+ 872	9
+ 79	3
+ 916	5
+ 238	6
+ 560	3
+ 988	1
+ 602	0
+ 639	0
+ 956	4
+ 823	9
+ 429	7
+ 446	8
+ 533	1
+ 346	7
+ 101	1
+ 883	10
+ 997	10
+ 307	9
+ 477	5
+ 495	0
+ 865	5
+ 135	5
+ 517	8
+ 479	5
+ 215	3
+ 399	6
+ 957	8
+ 454	5
+ 919	8
+ 168	0
+ 880	1
+ 992	9
+ 13	3
+ 791	5
+ 844	3
+ 527	7
+ 768	7
+ 176	3
+ 435	7
+ 759	7
+ 957	2
+ 295	9
+ 4	7
+ 403	9
+ 548	6
+ 943	4
+ 622	9
+ 305	6
+ 235	1
+ 124	1
+ 381	7
+ 789	1
+ 312	10
+ 434	7
+ 619	2
+ 398	6
+ 351	7
+ 489	4
+ 442	9
+ 279	10
+ 463	2
+ 418	1
+ 158	7
+ 720	4
+ 819	8
+ 473	2
+ 496	3
+ 349	8
+ 226	8
+ 556	8
+ 976	10
+ 421	3
+ 648	9
+ 683	1
+ 803	10
+ 80	3
+ 184	5
+ 352	3
+ 221	1
+ 736	0
+ 917	2
+ 240	4
+ 470	6
+ 221	7
+ 372	8
+ 542	3
+ 731	10
+ 676	4
+ 874	4
+ 469	7
+ 321	5
+ 943	5
+ 46	3
+ 848	3
+ 367	6
+ 307	3
+ 793	5
+ 697	3
+ 135	9
+ 959	5
+ 695	5
+ 855	4
+ 464	5
+ 806	3
+ 890	3
+ 14	2
+ 822	10
+ 715	9
+ 253	6
+ 135	6
+ 147	4
+ 904	9
+ 988	6
+ 203	1
+ 519	2
+ 630	2
+ 663	5
+ 640	1
+ 16	4
+ 465	9
+ 720	5
+ 115	5
+ 437	8
+ 410	7
+ 393	5
+ 309	5
+ 987	2
+ 479	10
+ 814	7
+ 97	3
+ 844	7
+ 547	5
+ 212	2
+ 634	2
+ 634	1
+ 133	4
+ 579	2
+ 896	0
+ 79	3
+ 706	5
+ 852	0
+ 12	8
+ 228	5
+ 813	0
+ 173	9
+ 376	0
+ 637	9
+ 524	8
+ 111	2
+ 76	7
+ 258	2
+ 98	8
+ 457	10
+ 853	5
+ 301	6
+ 8	2
+ 574	0
+ 991	8
+ 511	8
+ 845	7
+ 713	2
+ 702	4
+ 144	2
+ 199	3
+ 385	3
+ 999	6
+ 483	1
+ 481	9
+ 91	3
+ 475	4
+ 893	5
+ 544	5
+ 503	5
+ 270	0
+ 338	1
+ 698	1
+ 336	4
+ 402	5
+ 626	6
+ 735	0
+ 875	7
+ 655	4
+ 830	1
+ 298	9
+ 469	8
+ 313	4
+ 256	9
+ 830	8
+ 392	1
+ 773	7
+ 215	5
+ 782	6
+ 871	2
+ 31	5
+ 784	8
+ 509	7
+ 499	2
+ 17	3
+ 299	3
+ 250	8
+ 89	6
+ 130	3
+ 421	10
+ 104	8
+ 59	9
+ 543	3
+ 348	3
+ 824	2
+ 508	9
+ 717	3
+ 620	2
+ 950	1
+ 390	10
+ 448	7
+ 282	7
+ 457	4
+ 262	6
+ 716	7
+ 546	8
+ 496	6
+ 697	0
+ 879	0
+ 363	7
+ 265	9
+ 557	10
+ 163	2
+ 209	1
+ 296	6
+ 80	7
+ 288	4
+ 442	7
+ 733	7
+ 332	4
+ 387	9
+ 269	9
+ 483	10
+ 921	4
+ 12	3
+ 64	3
+ 155	6
+ 260	3
+ 799	5
+ 431	1
+ 68	5
+ 839	4
+ 873	3
+ 101	6
+ 986	4
+ 55	4
+ 311	3
+ 255	8
+ 290	2
+ 155	3
+ 460	2
+ 579	6
+ 840	8
+ 933	6
+ 308	4
+ 735	4
+ 875	6
+ 733	7
+ 855	8
+ 353	8
+ 268	4
+ 213	6
+ 732	5
+ 372	0
+ 644	5
+ 324	1
+ 746	9
+ 718	6
+ 743	7
+ 225	1
+ 15	10
+ 428	9
+ 534	2
+ 637	4
+ 996	10
+ 230	3
+ 399	4
+ 842	1
+ 911	2
+ 153	6
+ 741	5
+ 658	5
+ 380	4
+ 72	1
+ 28	3
+ 174	0
+ 258	6
+ 933	8
+ 763	6
+ 181	8
+ 561	4
+ 22	10
+ 854	9
+ 90	8
+ 78	2
+ 320	8
+ 719	10
+ 305	1
+ 354	4
+ 222	4
+ 675	4
+ 425	9
+ 997	4
+ 725	8
+ 928	9
+ 518	5
+ 317	5
+ 447	2
+ 405	5
+ 936	5
+ 780	3
+ 302	5
+ 233	6
+ 598	6
+ 985	8
+ 969	7
+ 215	4
+ 594	2
+ 752	3
+ 973	7
+ 224	5
+ 167	5
+ 32	6
+ 712	4
+ 152	6
+ 920	9
+ 903	2
+ 430	1
+ 830	0
+ 724	8
+ 848	7
+ 477	1
+ 88	1
+ 276	8
+ 389	2
+ 519	6
+ 740	7
+ 154	8
+ 301	9
+ 209	5
+ 514	1
+ 385	4
+ 351	8
+ 553	2
+ 843	3
+ 998	7
+ 971	5
+ 754	1
+ 545	0
+ 898	9
+ 279	4
+ 547	0
+ 104	7
+ 791	4
+ 568	10
+ 858	1
+ 129	2
+ 499	5
+ 58	1
+ 662	9
+ 330	7
+ 592	3
+ 134	3
+ 359	7
+ 376	3
+ 613	7
+ 675	2
+ 674	8
+ 862	5
+ 183	4
+ 465	0
+ 512	6
+ 284	0
+ 74	3
+ 63	7
+ 244	4
+ 395	8
+ 693	5
+ 181	1
+ 208	6
+ 309	8
+ 212	10
+ 981	9
+ 763	8
+ 352	9
+ 273	8
+ 988	8
+ 410	3
+ 796	5
+ 614	9
+ 220	9
+ 251	6
+ 693	9
+ 144	9
+ 995	4
+ 432	3
+ 174	6
+ 289	2
+ 531	1
+ 998	9
+ 997	3
+ 700	10
+ 57	1
+ 257	9
+ 594	9
+ 711	8
+ 730	10
+ 429	4
+ 905	6
+ 298	9
+ 926	7
+ 205	1
+ 374	5
+ 254	9
+ 545	3
+ 788	5
+ 524	5
+ 528	6
+ 598	8
+ 433	2
+ 656	1
+ 6	4
+ 105	4
+ 809	0
+ 8	1
+ 910	9
+ 836	1
+ 34	2
+ 608	3
+ 115	2
+ 541	9
+ 696	1
+ 391	2
+ 645	10
+ 8	1
+ 180	7
+ 220	2
+ 51	3
+ 621	9
+ 335	6
+ 966	2
+ 564	8
+ 359	6
+ 12	10
+ 887	1
+ 120	4
+ 31	8
+ 492	4
+ 40	1
+ 410	0
+ 213	6
+ 713	4
+ 777	8
+ 759	4
+ 623	1
+ 28	6
+ 338	6
+ 390	7
+ 191	4
+ 663	1
+ 530	8
+ 505	6
+ 599	10
+ 983	6
+ 133	4
+ 687	3
+ 984	4
+ 780	8
+ 163	5
+ 160	8
+ 632	2
+ 374	10
+ 780	8
+ 666	10
+ 167	3
+ 48	7
+ 112	6
+ 258	7
+ 549	2
+ 350	7
+ 635	0
+ 27	6
+ 437	8
+ 380	6
+ 345	5
+ 386	10
+ 727	8
+ 947	5
+ 525	6
+ 477	7
+ 942	5
+ 389	1
+ 77	6
+ 765	6
+ 889	1
+ 308	5
+ 153	3
+ 142	6
+ 143	5
+ 191	5
+ 62	6
+ 465	8
+ 338	4
+ 296	9
+ 25	8
+ 555	10
+ 298	9
+ 20	4
+ 591	8
+ 2	5
+ 901	3
+ 3	1
+ 645	1
+ 645	8
+ 667	8
+ 276	7
+ 413	7
+ 517	8
+ 153	8
+ 613	2
+ 586	2
+ 144	9
+ 112	2
+ 259	7
+ 949	3
+ 183	9
+ 570	2
+ 904	2
+ 331	5
+ 3	10
+ 385	3
+ 726	8
+ 20	2
+ 549	2
+ 56	2
+ 351	6
+ 330	5
+ 525	4
+ 658	8
+ 144	6
+ 45	3
+ 458	6
+ 513	4
+ 830	8
+ 911	8
+ 841	3
+ 112	6
+ 94	1
+ 810	6
+ 305	9
+ 806	7
+ 508	1
+ 150	0
+ 577	8
+ 817	7
+ 416	9
+ 49	9
+ 477	6
+ 236	3
+ 405	1
+ 140	2
+ 443	3
+ 812	5
+ 385	6
+ 181	7
+ 489	10
+ 345	10
+ 122	5
+ 30	7
+ 304	8
+ 421	7
+ 710	5
+ 594	2
+ 31	8
+ 493	4
+ 977	6
+ 681	4
+ 886	5
+ 958	3
+ 116	1
+ 960	6
+ 126	3
+ 602	2
+ 801	6
+ 948	1
+ 480	4
+ 825	2
+ 840	4
+ 376	9
+ 249	9
+ 307	2
+ 504	10
+ 646	4
+ 482	6
+ 661	6
+ 744	6
+ 203	9
+ 927	8
+ 118	7
+ 438	1
+ 833	9
+ 436	7
+ 108	3
+ 77	5
+ 147	3
+ 354	5
+ 552	9
+ 443	2
+ 248	9
+ 802	9
+ 523	5
+ 530	7
+ 416	5
+ 532	5
+ 186	10
+ 600	0
+ 887	0
+ 677	10
+ 312	8
+ 479	5
+ 80	8
+ 913	6
+ 691	4
+ 830	9
+ 281	6
+ 848	8
+ 178	4
+ 530	6
+ 835	1
+ 128	0
+ 31	7
+ 39	9
+ 765	7
+ 914	1
+ 470	4
+ 537	6
+ 226	4
+ 183	9
+ 806	0
+ 855	1
+ 645	7
+ 890	8
+ 81	4
+ 418	9
+ 482	5
+ 937	5
+ 274	10
+ 432	0
+ 692	3
+ 116	2
+ 738	7
+ 713	10
+ 102	9
+ 881	9
+ 909	7
+ 994	6
+ 439	9
+ 378	5
+ 304	8
+ 436	8
+ 341	4
+ 299	6
+ 349	7
+ 653	0
+ 76	8
+ 203	8
+ 421	9
+ 778	5
+ 812	7
+ 431	7
+ 395	4
+ 275	8
+ 309	7
+ 354	6
+ 449	8
+ 398	8
+ 163	7
+ 405	5
+ 428	1
+ 552	5
+ 828	8
+ 319	2
+ 672	1
+ 772	5
+ 756	2
+ 205	2
+ 628	5
+ 986	9
+ 134	3
+ 550	6
+ 130	9
+ 373	3
+ 644	8
+ 805	1
+ 837	4
+ 576	7
+ 113	9
+ 913	8
+ 992	7
+ 270	7
+ 889	5
+ 899	5
+ 956	9
+ 455	1
+ 225	0
+ 673	4
+ 952	0
+ 648	6
+ 825	5
+ 669	7
+ 811	2
+ 326	9
+ 140	2
+ 710	1
+ 925	10
+ 881	8
+ 454	8
+ 331	4
+ 665	8
+ 500	9
+ 791	2
+ 245	7
+ 219	9
+ 339	0
+ 347	0
+ 705	2
+ 253	0
+ 82	4
+ 270	8
+ 526	2
+ 771	4
+ 8	2
+ 186	3
+ 635	9
+ 126	1
+ 741	9
+ 307	10
+ 659	5
+ 878	10
+ 570	2
+ 5	3
+ 383	3
+ 306	5
+ 651	6
+ 256	2
+ 769	0
+ 583	8
+ 251	8
+ 117	9
+ 620	2
+ 21	4
+ 158	3
+ 346	8
+ 854	2
+ 814	4
+ 449	8
+ 699	8
+ 78	0
+ 296	7
+ 580	6
+ 905	3
+ 578	5
+ 127	8
+ 257	2
+ 715	9
+ 486	7
+ 237	6
+ 64	6
+ 461	9
+ 808	3
+ 342	3
+ 95	0
+ 89	2
+ 47	4
+ 901	6
+ 937	8
+ 977	5
+ 294	1
+ 344	6
+ 348	1
+ 428	8
+ 795	7
+ 478	9
+ 249	9
+ 777	1
+ 215	1
+ 314	3
+ 161	4
+ 482	2
+ 787	4
+ 835	7
+ 190	8
+ 238	5
+ 917	6
+ 36	3
+ 641	5
+ 100	4
+ 130	6
+ 295	4
+ 517	1
+ 436	7
+ 191	7
+ 42	4
+ 152	5
+ 559	9
+ 908	4
+ 663	1
+ 207	9
+ 583	1
+ 483	6
+ 390	1
+ 84	5
+ 561	2
+ 67	9
+ 593	6
+ 928	0
+ 316	1
+ 780	4
+ 470	9
+ 882	0
+ 871	8
+ 424	5
+ 888	6
+ 434	5
+ 756	9
+ 90	1
+ 42	2
+ 636	6
+ 387	7
+ 459	10
+ 288	4
+ 11	6
+ 505	8
+ 962	10
+ 722	8
+ 4	6
+ 634	4
+ 125	5
+ 59	6
+ 994	8
+ 476	1
+ 962	5
+ 257	6
+ 121	6
+ 301	6
+ 625	6
+ 966	6
+ 193	5
+ 426	2
+ 445	1
+ 1000	4
+ 739	6
+ 876	9
+ 157	9
+ 424	2
+ 751	9
+ 234	7
+ 418	5
+ 310	5
+ 135	6
+ 118	8
+ 200	1
+ 396	4
+ 555	8
+ 548	0
+ 969	5
+ 449	7
+ 183	3
+ 572	3
+ 261	10
+ 490	0
+ 896	7
+ 724	3
+ 214	0
+ 853	3
+ 645	10
+ 109	8
+ 56	5
+ 237	6
+ 326	8
+ 611	3
+ 334	1
+ 3	5
+ 385	6
+ 856	6
+ 571	3
+ 658	5
+ 69	4
+ 782	3
+ 415	6
+ 633	1
+ 607	7
+ 904	7
+ 248	1
+ 274	6
+ 927	9
+ 869	3
+ 945	9
+ 777	3
+ 447	6
+ 977	0
+ 978	6
+ 485	0
+ 16	3
+ 331	4
+ 902	10
+ 491	5
+ 707	4
+ 172	10
+ 537	4
+ 528	5
+ 331	4
+ 724	3
+ 268	5
+ 607	7
+ 134	6
+ 733	1
+ 219	2
+ 159	2
+ 485	5
+ 666	4
+ 455	2
+ 897	2
+ 552	1
+ 116	1
+ 515	6
+ 552	8
+ 42	3
+ 123	3
+ 777	7
+ 25	9
+ 314	8
+ 23	5
+ 975	2
+ 767	5
+ 673	4
+ 849	1
+ 591	7
+ 290	1
+ 815	4
+ 232	3
+ 51	8
+ 176	1
+ 61	3
+ 403	8
+ 28	4
+ 748	3
+ 185	8
+ 875	2
+ 953	6
+ 621	6
+ 76	5
+ 754	7
+ 216	0
+ 810	0
+ 451	0
+ 360	5
+ 826	5
+ 596	9
+ 834	10
+ 724	9
+ 426	5
+ 205	6
+ 244	1
+ 771	2
+ 724	4
+ 823	8
+ 863	6
+ 466	1
+ 622	3
+ 109	1
+ 318	5
+ 576	1
+ 6	2
+ 30	8
+ 170	8
+ 702	6
+ 226	9
+ 207	5
+ 989	10
+ 667	7
+ 372	5
+ 512	2
+ 67	10
+ 313	7
+ 254	4
+ 762	6
+ 892	3
+ 715	9
+ 510	7
+ 738	7
+ 498	4
+ 276	7
+ 348	5
+ 194	3
+ 462	9
+ 49	8
+ 350	6
+ 68	4
+ 539	4
+ 106	8
+ 804	9
+ 365	7
+ 207	1
+ 595	7
+ 824	3
+ 397	3
+ 773	7
+ 47	1
+ 156	2
+ 457	6
+ 101	5
+ 452	5
+ 66	5
+ 869	6
+ 902	10
+ 397	7
+ 844	8
+ 403	1
+ 841	10
+ 768	7
+ 330	2
+ 988	1
+ 837	0
+ 223	10
+ 276	7
+ 611	4
+ 185	1
+ 829	3
+ 583	7
+ 855	5
+ 672	3
+ 190	5
+ 14	6
+ 567	9
+ 590	3
+ 521	9
+ 498	5
+ 22	3
+ 544	2
+ 328	8
+ 925	9
+ 197	1
+ 1	0
+ 361	6
+ 723	2
+ 68	4
+ 469	3
+ 911	5
+ 851	5
+ 338	4
+ 812	9
+ 361	3
+ 368	4
+ 645	9
+ 629	10
+ 732	6
+ 911	9
+ 663	9
+ 955	0
+ 495	7
+ 241	6
+ 74	7
+ 820	10
+ 192	7
+ 462	5
+ 112	3
+ 388	5
+ 584	8
+ 856	2
+ 667	5
+ 201	4
+ 38	1
+ 329	7
+ 24	3
+ 726	5
+ 963	10
+ 81	0
+ 676	9
+ 21	9
+ 573	5
+ 398	7
+ 757	8
+ 157	3
+ 542	0
+ 569	2
+ 498	8
+ 608	5
+ 882	9
+ 238	9
+ 221	10
+ 424	2
+ 931	5
+ 221	6
+ 407	2
+ 476	10
+ 725	9
+ 664	5
+ 660	8
+ 822	2
+ 835	4
+ 411	3
+ 160	0
+ 870	0
+ 956	1
+ 947	2
+ 73	4
+ 362	0
+ 877	6
+ 612	3
+ 824	1
+ 265	5
+ 963	9
+ 31	6
+ 751	9
+ 825	6
+ 243	2
+ 920	4
+ 256	8
+ 445	2
+ 898	4
+ 390	10
+ 764	8
+ 975	6
+ 335	6
+ 926	2
+ 675	2
+ 708	6
+ 121	7
+ 261	9
+ 592	1
+ 458	8
+ 323	4
+ 238	6
+ 167	7
+ 791	1
+ 75	2
+ 36	8
+ 933	0
+ 481	3
+ 597	4
+ 427	3
+ 598	1
+ 910	7
+ 874	2
+ 590	5
+ 258	0
+ 300	6
+ 425	5
+ 160	6
+ 221	10
+ 657	3
+ 131	7
+ 134	1
+ 703	6
+ 332	3
+ 22	8
+ 573	6
+ 894	5
+ 339	8
+ 655	9
+ 234	9
+ 978	5
+ 494	4
+ 73	7
+ 995	3
+ 603	7
+ 588	7
+ 345	7
+ 799	0
+ 338	1
+ 349	4
+ 889	9
+ 980	8
+ 404	3
+ 551	1
+ 249	8
+ 972	2
+ 319	5
+ 629	4
+ 118	6
+ 685	7
+ 277	3
+ 456	6
+ 996	3
+ 670	3
+ 385	0
+ 694	3
+ 940	7
+ 57	3
+ 993	6
+ 404	2
+ 392	4
+ 468	7
+ 840	1
+ 103	10
+ 721	8
+ 680	10
+ 61	1
+ 620	1
+ 392	3
+ 391	8
+ 310	1
+ 51	3
+ 759	1
+ 595	8
+ 716	10
+ 993	1
+ 374	5
+ 819	2
+ 558	9
+ 172	3
+ 710	9
+ 278	8
+ 989	9
+ 829	4
+ 188	2
+ 158	5
+ 305	2
+ 748	1
+ 317	3
+ 815	0
+ 341	8
+ 141	7
+ 270	10
+ 929	8
+ 883	1
+ 108	6
+ 954	4
+ 364	9
+ 283	2
+ 324	5
+ 413	5
+ 970	7
+ 691	7
+ 781	0
+ 61	6
+ 41	4
+ 405	2
+ 118	7
+ 142	0
+ 504	0
+ 148	6
+ 618	1
+ 997	10
+ 46	3
+ 175	4
+ 752	6
+ 852	7
+ 306	5
+ 440	1
+ 552	5
+ 684	6
+ 904	1
+ 775	0
+ 764	9
+ 68	3
+ 942	2
+ 880	6
+ 319	9
+ 542	4
+ 157	7
+ 734	9
+ 306	6
+ 632	6
+ 130	1
+ 699	7
+ 574	4
+ 275	5
+ 472	1
+ 500	2
+ 968	6
+ 505	9
+ 785	4
+ 470	1
+ 261	0
+ 468	4
+ 730	2
+ 328	0
+ 788	10
+ 648	9
+ 32	3
+ 601	6
+ 730	9
+ 83	2
+ 926	6
+ 439	9
+ 151	9
+ 803	9
+ 327	3
+ 39	6
+ 285	5
+ 7	0
+ 709	3
+ 52	5
+ 294	7
+ 416	3
+ 47	0
+ 931	8
+ 892	0
+ 979	8
+ 597	4
+ 712	7
+ 361	5
+ 685	7
+ 789	7
+ 277	1
+ 231	3
+ 89	9
+ 618	1
+ 437	9
+ 840	9
+ 237	9
+ 869	2
+ 664	8
+ 181	6
+ 580	8
+ 61	3
+ 527	4
+ 808	2
+ 110	6
+ 936	4
+ 671	2
+ 670	8
+ 106	3
+ 901	5
+ 199	7
+ 395	4
+ 629	3
+ 603	3
+ 26	8
+ 936	6
+ 562	10
+ 898	1
+ 418	7
+ 301	5
+ 303	2
+ 915	10
+ 403	6
+ 733	5
+ 873	6
+ 52	1
+ 376	4
+ 508	0
+ 712	1
+ 297	7
+ 894	2
+ 344	5
+ 229	2
+ 546	6
+ 948	8
+ 176	3
+ 84	1
+ 225	5
+ 677	10
+ 996	5
+ 592	0
+ 622	10
+ 495	1
+ 972	2
+ 240	3
+ 944	1
+ 502	3
+ 591	7
+ 530	1
+ 379	5
+ 984	6
+ 730	1
+ 646	10
+ 555	3
+ 912	6
+ 873	5
+ 598	5
+ 472	1
+ 625	4
+ 299	9
+ 713	2
+ 1000	2
+ 531	6
+ 946	1
+ 728	3
+ 540	7
+ 881	3
+ 781	5
+ 223	3
+ 850	1
+ 885	7
+ 639	5
+ 218	1
+ 576	8
+ 555	9
+ 707	3
+ 120	7
+ 483	7
+ 298	4
+ 712	0
+ 755	3
+ 739	6
+ 521	5
+ 162	7
+ 854	0
+ 880	7
+ 735	5
+ 223	10
+ 630	8
+ 795	2
+ 676	5
+ 454	8
+ 208	9
+ 446	5
+ 367	2
+ 532	1
+ 410	3
+ 757	9
+ 789	9
+ 676	6
+ 931	6
+ 384	7
+ 74	6
+ 618	7
+ 407	4
+ 890	1
+ 915	3
+ 878	1
+ 281	3
+ 629	6
+ 482	2
+ 769	9
+ 431	5
+ 824	2
+ 445	5
+ 865	4
+ 55	2
+ 42	1
+ 855	7
+ 834	3
+ 73	7
+ 344	10
+ 68	2
+ 111	3
+ 545	7
+ 996	0
+ 901	8
+ 920	3
+ 291	7
+ 553	7
+ 243	4
+ 111	3
+ 666	2
+ 427	5
+ 812	3
+ 783	9
+ 985	1
+ 873	1
+ 348	10
+ 401	9
+ 724	4
+ 921	6
+ 163	8
+ 957	5
+ 584	5
+ 189	8
+ 928	3
+ 125	6
+ 452	6
+ 114	3
+ 814	9
+ 150	8
+ 23	0
+ 851	4
+ 7	3
+ 265	7
+ 650	2
+ 356	8
+ 25	3
+ 266	6
+ 824	5
+ 436	8
+ 754	6
+ 345	2
+ 114	5
+ 471	9
+ 356	6
+ 726	4
+ 644	6
+ 751	7
+ 829	0
+ 383	5
+ 201	7
+ 291	2
+ 53	6
+ 836	9
+ 11	3
+ 628	8
+ 834	10
+ 972	9
+ 433	4
+ 875	8
+ 63	6
+ 170	7
+ 178	9
+ 359	0
+ 937	7
+ 487	1
+ 481	8
+ 365	5
+ 335	2
+ 411	3
+ 472	0
+ 112	3
+ 13	1
+ 254	4
+ 526	1
+ 236	6
+ 730	4
+ 297	9
+ 327	7
+ 916	3
+ 399	4
+ 402	9
+ 181	8
+ 414	5
+ 967	8
+ 862	4
+ 865	10
+ 745	9
+ 58	10
+ 325	6
+ 128	6
+ 173	9
+ 967	5
+ 767	3
+ 127	7
+ 558	5
+ 86	10
+ 405	3
+ 726	8
+ 783	7
+ 645	6
+ 132	5
+ 619	9
+ 388	7
+ 876	7
+ 260	0
+ 273	4
+ 862	2
+ 903	6
+ 534	0
+ 312	1
+ 555	4
+ 51	10
+ 665	8
+ 780	4
+ 469	4
+ 93	6
+ 934	7
+ 477	3
+ 388	4
+ 34	6
+ 356	3
+ 81	2
+ 546	10
+ 847	1
+ 15	2
+ 171	6
+ 558	2
+ 531	2
+ 998	3
+ 672	5
+ 735	8
+ 67	7
+ 476	5
+ 991	9
+ 897	0
+ 512	3
+ 332	6
+ 471	9
+ 578	3
+ 958	6
+ 477	1
+ 163	0
+ 351	7
+ 259	3
+ 4	9
+ 816	7
+ 695	9
+ 409	2
+ 427	4
+ 35	3
+ 426	5
+ 576	8
+ 140	0
+ 636	7
+ 365	6
+ 311	8
+ 724	5
+ 877	1
+ 167	1
+ 424	2
+ 67	2
+ 911	8
+ 122	3
+ 932	5
+ 721	10
+ 872	1
+ 514	4
+ 905	7
+ 495	5
+ 372	9
+ 135	7
+ 702	9
+ 156	6
+ 933	3
+ 715	4
+ 495	8
+ 596	4
+ 543	7
+ 726	5
+ 267	4
+ 442	1
+ 595	10
+ 588	5
+ 610	1
+ 40	10
+ 943	2
+ 665	6
+ 34	8
+ 224	10
+ 145	9
+ 324	6
+ 721	9
+ 45	3
+ 638	8
+ 739	9
+ 219	2
+ 45	8
+ 138	6
+ 314	7
+ 717	4
+ 730	7
+ 529	4
+ 305	6
+ 216	5
+ 531	4
+ 468	9
+ 0	2
+ 776	0
+ 453	4
+ 817	2
+ 320	0
+ 373	4
+ 850	5
+ 998	2
+ 259	7
+ 518	10
+ 375	0
+ 384	7
+ 611	6
+ 209	1
+ 961	7
+ 998	10
+ 866	8
+ 7	3
+ 188	8
+ 511	5
+ 861	9
+ 873	7
+ 395	9
+ 875	7
+ 586	4
+ 643	10
+ 441	0
+ 642	1
+ 628	9
+ 195	6
+ 529	2
+ 551	4
+ 967	6
+ 714	2
+ 383	2
+ 663	2
+ 109	5
+ 955	5
+ 408	8
+ 158	10
+ 223	8
+ 956	7
+ 829	6
+ 717	5
+ 449	9
+ 46	10
+ 104	6
+ 373	1
+ 156	1
+ 226	5
+ 313	9
+ 781	4
+ 426	7
+ 926	8
+ 566	1
+ 830	8
+ 886	8
+ 454	7
+ 384	2
+ 172	8
+ 82	2
+ 811	2
+ 816	2
+ 257	10
+ 272	5
+ 509	6
+ 373	3
+ 6	8
+ 27	9
+ 635	6
+ 16	5
+ 382	9
+ 250	8
+ 617	6
+ 7	8
+ 468	1
+ 7	3
+ 275	8
+ 464	5
+ 794	7
+ 16	3
+ 320	4
+ 593	3
+ 189	6
+ 259	8
+ 213	3
+ 288	6
+ 177	5
+ 431	8
+ 173	4
+ 583	6
+ 527	6
+ 920	8
+ 413	4
+ 335	2
+ 120	4
+ 509	4
+ 740	1
+ 767	9
+ 722	0
+ 754	9
+ 301	0
+ 530	5
+ 581	10
+ 273	8
+ 400	9
+ 395	9
+ 446	3
+ 728	9
+ 700	1
+ 65	8
+ 414	6
+ 260	2
+ 676	0
+ 84	4
+ 52	8
+ 334	4
+ 880	9
+ 832	5
+ 826	1
+ 216	2
+ 960	6
+ 152	4
+ 927	9
+ 265	6
+ 943	3
+ 446	4
+ 904	7
+ 511	6
+ 733	6
+ 979	8
+ 433	3
+ 138	3
+ 177	10
+ 775	0
+ 74	10
+ 227	0
+ 603	4
+ 441	5
+ 259	7
+ 157	2
+ 37	6
+ 559	9
+ 309	1
+ 522	0
+ 666	5
+ 827	1
+ 814	10
+ 413	10
+ 935	2
+ 993	0
+ 180	2
+ 44	8
+ 599	5
+ 312	9
+ 191	5
+ 61	2
+ 73	6
+ 169	4
+ 691	7
+ 424	4
+ 192	3
+ 456	0
+ 217	9
+ 997	2
+ 57	10
+ 162	2
+ 210	2
+ 19	8
+ 690	3
+ 668	9
+ 801	7
+ 109	9
+ 350	3
+ 256	0
+ 969	7
+ 399	2
+ 932	9
+ 168	1
+ 724	2
+ 301	8
+ 154	5
+ 19	4
+ 668	0
+ 173	4
+ 370	8
+ 239	2
+ 570	3
+ 45	9
+ 626	3
+ 962	6
+ 982	4
+ 757	9
+ 216	9
+ 63	9
+ 89	4
+ 722	2
+ 828	7
+ 606	5
+ 779	8
+ 854	1
+ 619	1
+ 320	2
+ 441	4
+ 110	1
+ 666	1
+ 663	6
+ 432	4
+ 562	6
+ 344	6
+ 588	4
+ 989	3
+ 676	8
+ 51	3
+ 313	8
+ 61	2
+ 978	7
+ 260	3
+ 870	7
+ 663	10
+ 768	3
+ 50	4
+ 978	5
+ 850	5
+ 129	2
+ 165	7
+ 628	2
+ 27	3
+ 971	1
+ 586	3
+ 907	6
+ 450	9
+ 327	7
+ 184	2
+ 410	8
+ 175	2
+ 177	2
+ 608	2
+ 707	5
+ 694	8
+ 652	9
+ 554	3
+ 13	6
+ 584	10
+ 658	2
+ 267	6
+ 816	7
+ 450	1
+ 428	6
+ 339	8
+ 480	5
+ 16	7
+ 739	6
+ 811	4
+ 82	5
+ 283	7
+ 364	8
+ 15	4
+ 417	6
+ 360	1
+ 769	6
+ 640	6
+ 345	1
+ 728	8
+ 723	1
+ 611	2
+ 581	6
+ 861	3
+ 252	7
+ 767	3
+ 177	1
+ 69	5
+ 887	1
+ 918	3
+ 684	3
+ 380	5
+ 906	0
+ 38	3
+ 110	8
+ 24	8
+ 833	6
+ 37	4
+ 263	9
+ 733	5
+ 570	5
+ 849	7
+ 550	9
+ 288	4
+ 2	2
+ 742	7
+ 484	1
+ 139	4
+ 142	2
+ 640	3
+ 942	7
+ 85	8
+ 300	1
+ 188	6
+ 20	9
+ 76	6
+ 422	9
+ 336	10
+ 843	6
+ 409	8
+ 830	2
+ 531	3
+ 274	7
+ 704	4
+ 846	3
+ 667	8
+ 8	8
+ 564	3
+ 874	8
+ 870	9
+ 674	9
+ 483	1
+ 871	8
+ 68	7
+ 444	5
+ 559	3
+ 629	1
+ 588	9
+ 760	3
+ 318	6
+ 636	10
+ 395	6
+ 737	10
+ 951	6
+ 711	8
+ 505	4
+ 767	10
+ 480	6
+ 807	5
+ 353	3
+ 25	9
+ 525	7
+ 2	1
+ 555	8
+ 405	9
+ 369	0
+ 858	8
+ 684	6
+ 723	6
+ 207	4
+ 456	7
+ 818	2
+ 701	3
+ 862	5
+ 845	2
+ 759	9
+ 127	3
+ 523	1
+ 397	1
+ 892	8
+ 952	3
+ 841	8
+ 25	5
+ 406	7
+ 160	6
+ 181	6
+ 325	10
+ 840	0
+ 297	7
+ 534	1
+ 916	3
+ 13	0
+ 577	5
+ 172	10
+ 615	1
+ 775	6
+ 325	6
+ 377	3
+ 141	8
+ 97	3
+ 396	3
+ 918	7
+ 278	8
+ 747	6
+ 459	3
+ 717	4
+ 574	7
+ 418	2
+ 265	6
+ 125	9
+ 655	9
+ 447	10
+ 516	8
+ 329	7
+ 606	4
+ 959	0
+ 705	9
+ 723	10
+ 634	5
+ 557	1
+ 751	3
+ 468	3
+ 4	9
+ 477	3
+ 477	6
+ 149	1
+ 501	6
+ 111	0
+ 419	4
+ 674	0
+ 867	6
+ 28	6
+ 509	8
+ 554	1
+ 221	1
+ 236	10
+ 385	7
+ 298	4
+ 590	8
+ 657	1
+ 375	8
+ 200	9
+ 402	3
+ 893	8
+ 752	6
+ 847	6
+ 199	9
+ 190	7
+ 626	7
+ 852	8
+ 854	1
+ 819	2
+ 792	1
+ 627	4
+ 891	3
+ 450	3
+ 91	6
+ 142	5
+ 961	0
+ 315	7
+ 602	2
+ 331	8
+ 37	5
+ 510	7
+ 265	4
+ 509	1
+ 450	3
+ 358	2
+ 445	10
+ 624	3
+ 270	1
+ 602	4
+ 724	7
+ 854	7
+ 779	2
+ 397	4
+ 331	7
+ 182	4
+ 248	7
+ 31	5
+ 55	5
+ 632	5
+ 869	10
+ 746	3
+ 976	4
+ 649	2
+ 445	3
+ 606	2
+ 995	5
+ 853	8
+ 629	2
+ 155	10
+ 977	3
+ 329	2
+ 30	4
+ 738	1
+ 901	4
+ 589	8
+ 361	3
+ 84	3
+ 706	7
+ 582	2
+ 984	2
+ 319	10
+ 648	2
+ 753	3
+ 421	9
+ 237	4
+ 246	6
+ 623	3
+ 926	4
+ 361	8
+ 732	9
+ 597	1
+ 285	7
+ 430	10
+ 414	0
+ 141	4
+ 201	5
+ 378	8
+ 631	1
+ 126	1
+ 40	4
+ 449	3
+ 929	1
+ 562	9
+ 433	9
+ 682	2
+ 872	3
+ 258	2
+ 960	7
+ 147	4
+ 700	3
+ 773	9
+ 747	2
+ 749	4
+ 282	9
+ 429	3
+ 239	9
+ 608	2
+ 949	2
+ 23	4
+ 92	7
+ 546	10
+ 984	8
+ 121	9
+ 491	3
+ 318	2
+ 556	1
+ 91	3
+ 242	8
+ 680	5
+ 716	1
+ 846	10
+ 986	5
+ 123	9
+ 624	1
+ 317	7
+ 851	9
+ 681	8
+ 668	8
+ 778	2
+ 71	1
+ 350	6
+ 186	4
+ 929	4
+ 282	6
+ 952	10
+ 718	8
+ 952	7
+ 252	1
+ 639	9
+ 221	10
+ 593	1
+ 820	3
+ 906	5
+ 76	7
+ 647	1
+ 779	10
+ 774	10
+ 438	7
+ 393	7
+ 312	3
+ 718	0
+ 143	7
+ 734	4
+ 745	4
+ 271	10
+ 330	9
+ 37	1
+ 138	9
+ 637	2
+ 627	3
+ 361	4
+ 281	1
+ 372	7
+ 838	8
+ 440	1
+ 110	2
+ 180	3
+ 828	9
+ 647	6
+ 287	9
+ 538	6
+ 782	6
+ 766	9
+ 518	4
+ 133	1
+ 688	5
+ 551	10
+ 629	9
+ 689	5
+ 688	1
+ 616	8
+ 287	8
+ 50	1
+ 709	7
+ 687	10
+ 616	2
+ 613	4
+ 801	4
+ 316	3
+ 782	4
+ 464	5
+ 944	0
+ 439	6
+ 939	1
+ 39	6
+ 257	7
+ 425	5
+ 451	5
+ 658	2
+ 173	3
+ 157	8
+ 570	8
+ 186	4
+ 148	5
+ 690	9
+ 951	2
+ 400	9
+ 170	8
+ 468	1
+ 967	5
+ 735	2
+ 162	2
+ 768	6
+ 635	4
+ 774	8
+ 771	9
+ 596	3
+ 700	8
+ 712	8
+ 283	4
+ 778	2
+ 556	2
+ 129	7
+ 17	6
+ 834	10
+ 104	6
+ 208	3
+ 729	10
+ 879	4
+ 403	7
+ 171	2
+ 583	8
+ 516	3
+ 548	2
+ 131	8
+ 631	9
+ 66	2
+ 86	2
+ 912	1
+ 792	7
+ 86	9
+ 315	3
+ 161	0
+ 272	0
+ 408	7
+ 693	6
+ 850	3
+ 346	4
+ 559	9
+ 595	7
+ 726	2
+ 598	8
+ 412	7
+ 987	3
+ 786	8
+ 70	9
+ 676	4
+ 167	8
+ 430	4
+ 877	8
+ 113	6
+ 417	10
+ 846	8
+ 330	4
+ 657	9
+ 94	4
+ 150	7
+ 175	6
+ 376	2
+ 886	2
+ 942	10
+ 34	6
+ 342	2
+ 455	8
+ 639	3
+ 610	8
+ 902	0
+ 715	7
+ 789	0
+ 152	4
+ 969	2
+ 829	1
+ 938	0
+ 682	3
+ 166	6
+ 475	1
+ 525	5
+ 725	9
+ 709	2
+ 639	3
+ 511	2
+ 99	4
+ 275	8
+ 160	1
+ 859	3
+ 509	8
+ 558	3
+ 948	5
+ 341	6
+ 809	5
+ 198	3
+ 614	7
+ 792	3
+ 590	5
+ 519	2
+ 848	0
+ 478	9
+ 443	8
+ 761	6
+ 816	6
+ 916	3
+ 448	5
+ 663	4
+ 970	0
+ 26	8
+ 512	2
+ 62	1
+ 947	9
+ 465	5
+ 354	10
+ 766	2
+ 14	2
+ 149	5
+ 996	9
+ 61	8
+ 530	10
+ 138	10
+ 451	8
+ 374	4
+ 806	4
+ 199	3
+ 623	3
+ 443	6
+ 115	9
+ 107	5
+ 893	9
+ 671	9
+ 117	8
+ 365	1
+ 730	4
+ 926	3
+ 403	1
+ 237	9
+ 865	6
+ 275	7
+ 10	5
+ 988	6
+ 736	4
+ 204	9
+ 340	3
+ 321	2
+ 185	10
+ 140	3
+ 812	5
+ 416	5
+ 931	3
+ 802	3
+ 405	0
+ 189	3
+ 650	5
+ 941	7
+ 939	9
+ 295	7
+ 361	5
+ 526	7
+ 810	8
+ 934	10
+ 839	1
+ 297	7
+ 579	7
+ 194	5
+ 54	10
+ 845	5
+ 36	0
+ 730	7
+ 498	7
+ 346	4
+ 602	6
+ 112	10
+ 140	6
+ 665	9
+ 486	6
+ 945	3
+ 673	2
+ 977	3
+ 954	2
+ 763	0
+ 167	6
+ 468	2
+ 641	2
+ 888	1
+ 870	2
+ 576	5
+ 876	7
+ 434	0
+ 326	1
+ 965	8
+ 698	9
+ 136	4
+ 151	1
+ 624	1
+ 284	4
+ 114	5
+ 994	6
+ 654	6
+ 780	5
+ 773	7
+ 777	3
+ 122	7
+ 36	6
+ 669	4
+ 655	6
+ 174	4
+ 544	3
+ 724	7
+ 423	3
+ 801	7
+ 734	9
+ 158	7
+ 497	8
+ 362	3
+ 354	1
+ 928	1
+ 484	0
+ 784	5
+ 605	5
+ 882	3
+ 87	1
+ 613	6
+ 365	3
+ 326	8
+ 685	1
+ 495	4
+ 42	7
+ 148	5
+ 465	5
+ 816	8
+ 646	7
+ 950	1
+ 793	7
+ 649	4
+ 187	5
+ 658	3
+ 587	3
+ 904	10
+ 608	2
+ 740	3
+ 356	2
+ 712	4
+ 888	9
+ 937	4
+ 370	8
+ 172	0
+ 497	1
+ 146	3
+ 855	8
+ 687	0
+ 327	3
+ 315	9
+ 616	2
+ 866	2
+ 449	6
+ 516	8
+ 842	2
+ 203	7
+ 89	1
+ 83	5
+ 893	3
+ 475	4
+ 376	6
+ 679	2
+ 416	4
+ 272	7
+ 711	6
+ 656	3
+ 806	5
+ 550	3
+ 129	1
+ 60	10
+ 295	3
+ 701	4
+ 403	8
+ 843	3
+ 39	3
+ 686	4
+ 940	4
+ 645	4
+ 732	9
+ 99	4
+ 504	8
+ 770	3
+ 278	3
+ 565	4
+ 386	6
+ 377	7
+ 887	1
+ 65	3
+ 861	9
+ 586	9
+ 227	3
+ 315	2
+ 638	10
+ 523	4
+ 878	6
+ 812	4
+ 378	6
+ 693	7
+ 901	3
+ 62	3
+ 881	4
+ 968	8
+ 517	0
+ 58	4
+ 941	6
+ 278	2
+ 917	6
+ 335	6
+ 553	9
+ 923	4
+ 480	7
+ 813	9
+ 316	5
+ 514	2
+ 763	6
+ 504	6
+ 16	5
+ 413	5
+ 505	5
+ 911	4
+ 116	2
+ 614	0
+ 782	9
+ 587	3
+ 806	5
+ 767	3
+ 246	6
+ 145	6
+ 86	7
+ 780	8
+ 236	3
+ 494	3
+ 756	9
+ 785	3
+ 378	7
+ 707	5
+ 885	3
+ 527	7
+ 269	1
+ 4	1
+ 625	8
+ 362	9
+ 351	5
+ 433	4
+ 166	2
+ 287	4
+ 497	8
+ 655	3
+ 688	4
+ 514	1
+ 137	2
+ 561	0
+ 541	1
+ 690	8
+ 202	7
+ 885	8
+ 464	2
+ 698	8
+ 753	1
+ 252	9
+ 345	5
+ 322	8
+ 321	10
+ 95	0
+ 418	6
+ 76	6
+ 829	6
+ 576	4
+ 725	3
+ 180	9
+ 959	1
+ 755	4
+ 311	5
+ 238	1
+ 585	5
+ 983	9
+ 30	3
+ 772	4
+ 283	9
+ 360	7
+ 476	4
+ 256	3
+ 73	8
+ 675	8
+ 98	9
+ 726	1
+ 919	5
+ 480	2
+ 934	7
+ 293	5
+ 208	3
+ 450	2
+ 582	2
+ 588	9
+ 88	9
+ 567	6
+ 384	8
+ 869	5
+ 655	5
+ 255	8
+ 398	10
+ 810	3
+ 461	3
+ 546	4
+ 8	8
+ 915	2
+ 115	4
+ 453	7
+ 586	0
+ 562	7
+ 988	1
+ 238	4
+ 952	1
+ 829	6
+ 650	1
+ 359	0
+ 64	2
+ 365	5
+ 459	9
+ 920	5
+ 749	8
+ 683	9
+ 200	1
+ 561	8
+ 176	1
+ 460	2
+ 252	7
+ 537	2
+ 805	4
+ 810	5
+ 449	2
+ 503	5
+ 338	9
+ 37	8
+ 778	10
+ 264	5
+ 793	9
+ 390	10
+ 83	10
+ 778	3
+ 74	2
+ 424	3
+ 936	10
+ 530	7
+ 326	3
+ 196	8
+ 509	7
+ 287	8
+ 567	3
+ 645	3
+ 282	9
+ 871	1
+ 856	3
+ 68	9
+ 212	8
+ 198	3
+ 84	6
+ 613	0
+ 583	1
+ 761	9
+ 484	10
+ 684	10
+ 657	10
+ 841	2
+ 296	5
+ 569	6
+ 395	4
+ 653	3
+ 702	7
+ 190	9
+ 567	4
+ 201	7
+ 11	8
+ 670	6
+ 957	4
+ 504	4
+ 389	2
+ 435	0
+ 160	3
+ 270	5
+ 761	8
+ 34	2
+ 279	7
+ 407	10
+ 408	6
+ 894	10
+ 986	1
+ 625	10
+ 908	3
+ 592	9
+ 727	1
+ 307	1
+ 285	7
+ 162	4
+ 17	4
+ 900	8
+ 270	9
+ 934	5
+ 622	3
+ 529	0
+ 939	4
+ 5	9
+ 518	6
+ 923	4
+ 925	5
+ 292	7
+ 612	6
+ 768	9
+ 341	9
+ 341	4
+ 362	2
+ 136	6
+ 175	1
+ 181	8
+ 411	7
+ 826	4
+ 134	8
+ 275	7
+ 461	2
+ 79	4
+ 714	4
+ 38	3
+ 970	8
+ 222	3
+ 737	6
+ 668	1
+ 803	8
+ 732	10
+ 873	9
+ 774	3
+ 623	6
+ 635	8
+ 431	9
+ 409	9
+ 108	5
+ 278	8
+ 858	3
+ 147	8
+ 123	4
+ 139	9
+ 931	8
+ 959	7
+ 611	7
+ 712	5
+ 604	5
+ 769	2
+ 86	4
+ 985	5
+ 313	4
+ 408	4
+ 881	7
+ 243	7
+ 2	4
+ 568	1
+ 760	7
+ 985	7
+ 514	9
+ 426	1
+ 636	1
+ 608	2
+ 624	4
+ 467	7
+ 780	5
+ 227	1
+ 846	6
+ 514	7
+ 321	8
+ 467	3
+ 148	0
+ 448	9
+ 742	4
+ 598	3
+ 378	0
+ 380	0
+ 162	10
+ 253	8
+ 365	7
+ 495	1
+ 173	7
+ 238	0
+ 356	8
+ 746	7
+ 510	2
+ 0	7
+ 248	4
+ 565	10
+ 882	2
+ 246	3
+ 187	6
+ 272	3
+ 614	5
+ 134	10
+ 246	6
+ 125	4
+ 350	4
+ 437	7
+ 115	2
+ 384	6
+ 396	4
+ 282	6
+ 833	8
+ 634	7
+ 10	9
+ 973	2
+ 507	2
+ 545	1
+ 771	7
+ 100	0
+ 307	2
+ 435	7
+ 588	9
+ 364	7
+ 55	7
+ 327	5
+ 132	6
+ 95	10
+ 455	7
+ 679	5
+ 609	7
+ 661	1
+ 897	2
+ 237	7
+ 885	3
+ 685	2
+ 563	1
+ 849	2
+ 991	2
+ 853	0
+ 961	2
+ 497	1
+ 788	6
+ 57	2
+ 320	7
+ 708	9
+ 387	4
+ 46	3
+ 575	3
+ 953	5
+ 620	6
+ 652	2
+ 758	5
+ 333	7
+ 714	2
+ 795	7
+ 365	3
+ 767	2
+ 883	8
+ 396	2
+ 559	1
+ 133	9
+ 472	2
+ 232	0
+ 461	2
+ 507	1
+ 823	2
+ 264	6
+ 659	6
+ 329	4
+ 783	1
+ 47	1
+ 416	8
+ 300	3
+ 638	7
+ 502	2
+ 800	6
+ 145	3
+ 814	4
+ 319	3
+ 561	8
+ 356	4
+ 984	6
+ 964	6
+ 218	3
+ 16	0
+ 418	1
+ 148	8
+ 878	4
+ 133	5
+ 144	6
+ 714	9
+ 270	9
+ 217	1
+ 235	5
+ 358	8
+ 362	7
+ 180	3
+ 335	1
+ 989	6
+ 437	0
+ 553	9
+ 69	7
+ 689	9
+ 149	8
+ 463	3
+ 457	2
+ 238	7
+ 36	5
+ 810	3
+ 990	2
+ 67	4
+ 883	2
+ 698	2
+ 390	7
+ 771	8
+ 693	3
+ 683	8
+ 26	4
+ 709	2
+ 194	2
+ 469	7
+ 349	7
+ 377	4
+ 161	2
+ 656	2
+ 355	7
+ 503	2
+ 969	2
+ 456	4
+ 889	2
+ 187	6
+ 553	9
+ 344	6
+ 241	1
+ 754	4
+ 225	2
+ 85	6
+ 929	5
+ 960	1
+ 650	6
+ 241	0
+ 339	7
+ 244	3
+ 946	7
+ 668	8
+ 928	9
+ 418	5
+ 725	8
+ 59	10
+ 815	8
+ 400	0
+ 35	5
+ 614	10
+ 948	6
+ 53	6
+ 190	3
+ 604	5
+ 39	8
+ 838	10
+ 547	5
+ 820	5
+ 361	2
+ 955	1
+ 0	0
+ 52	8
+ 826	5
+ 855	9
+ 938	5
+ 825	9
+ 43	9
+ 484	2
+ 173	1
+ 762	2
+ 935	6
+ 196	5
+ 107	0
+ 957	5
+ 254	9
+ 554	3
+ 926	6
+ 69	8
+ 58	9
+ 614	10
+ 393	4
+ 882	4
+ 317	4
+ 669	5
+ 454	4
+ 701	4
+ 32	9
+ 871	1
+ 913	8
+ 607	2
+ 740	2
+ 421	7
+ 767	5
+ 418	8
+ 414	0
+ 821	8
+ 470	7
+ 244	8
+ 69	9
+ 277	5
+ 345	10
+ 912	4
+ 875	8
+ 516	8
+ 611	1
+ 956	4
+ 285	4
+ 16	1
+ 867	4
+ 878	3
+ 466	7
+ 88	9
+ 401	3
+ 723	5
+ 245	0
+ 992	6
+ 978	9
+ 967	9
+ 687	5
+ 642	3
+ 607	6
+ 648	9
+ 974	7
+ 944	8
+ 98	8
+ 122	6
+ 520	2
+ 500	9
+ 541	2
+ 391	8
+ 223	4
+ 376	2
+ 288	3
+ 55	10
+ 827	7
+ 273	4
+ 294	9
+ 325	3
+ 585	3
+ 108	7
+ 92	2
+ 248	6
+ 440	7
+ 533	10
+ 971	9
+ 767	2
+ 308	1
+ 395	6
+ 487	4
+ 571	3
+ 146	8
+ 747	4
+ 765	1
+ 707	4
+ 342	8
+ 34	4
+ 46	3
+ 46	5
+ 30	6
+ 466	0
+ 504	2
+ 194	8
+ 378	6
+ 408	9
+ 38	10
+ 178	2
+ 823	9
+ 624	6
+ 997	3
+ 939	3
+ 147	10
+ 772	2
+ 255	8
+ 677	3
+ 397	1
+ 286	9
+ 378	5
+ 712	8
+ 69	1
+ 620	1
+ 98	8
+ 291	9
+ 722	9
+ 509	7
+ 245	4
+ 58	4
+ 421	8
+ 584	7
+ 648	3
+ 962	0
+ 405	2
+ 945	8
+ 727	7
+ 538	8
+ 776	2
+ 903	9
+ 956	2
+ 796	7
+ 108	3
+ 397	4
+ 753	5
+ 745	2
+ 285	3
+ 850	9
+ 591	8
+ 977	10
+ 59	9
+ 780	8
+ 578	3
+ 583	4
+ 476	5
+ 228	4
+ 679	0
+ 110	8
+ 329	5
+ 141	1
+ 963	9
+ 255	2
+ 215	1
+ 181	8
+ 917	2
+ 803	10
+ 80	6
+ 763	7
+ 900	3
+ 12	4
+ 831	2
+ 809	5
+ 264	9
+ 297	6
+ 427	4
+ 674	4
+ 324	9
+ 638	5
+ 34	8
+ 346	10
+ 978	1
+ 928	1
+ 730	7
+ 716	6
+ 36	7
+ 7	9
+ 969	8
+ 378	2
+ 735	7
+ 826	2
+ 113	5
+ 552	4
+ 429	2
+ 976	5
+ 10	3
+ 415	10
+ 470	3
+ 46	2
+ 33	8
+ 831	1
+ 490	8
+ 937	5
+ 654	3
+ 691	4
+ 990	5
+ 550	1
+ 17	1
+ 539	4
+ 292	5
+ 909	3
+ 837	3
+ 289	3
+ 666	3
+ 507	7
+ 96	3
+ 769	6
+ 176	7
+ 45	8
+ 21	7
+ 218	0
+ 253	8
+ 113	3
+ 870	7
+ 715	2
+ 167	6
+ 463	0
+ 947	8
+ 312	6
+ 87	8
+ 312	2
+ 157	1
+ 770	3
+ 787	8
+ 163	8
+ 551	4
+ 818	8
+ 150	9
+ 74	0
+ 583	8
+ 182	8
+ 414	6
+ 755	4
+ 397	1
+ 974	5
+ 886	3
+ 668	0
+ 368	4
+ 377	2
+ 253	5
+ 964	8
+ 921	8
+ 609	1
+ 713	7
+ 90	3
+ 472	3
+ 47	9
+ 917	8
+ 247	3
+ 869	2
+ 798	8
+ 508	5
+ 798	9
+ 905	2
+ 32	2
+ 714	10
+ 962	6
+ 777	6
+ 705	5
+ 253	8
+ 787	7
+ 67	8
+ 612	10
+ 636	9
+ 298	5
+ 80	1
+ 259	6
+ 563	1
+ 464	5
+ 232	5
+ 625	9
+ 491	6
+ 581	3
+ 157	3
+ 759	4
+ 82	5
+ 136	1
+ 380	7
+ 132	0
+ 607	4
+ 520	7
+ 526	8
+ 275	1
+ 837	7
+ 556	1
+ 235	2
+ 15	7
+ 768	6
+ 994	9
+ 882	8
+ 336	10
+ 299	5
+ 112	7
+ 220	2
+ 695	8
+ 675	2
+ 513	2
+ 995	8
+ 290	8
+ 527	8
+ 900	8
+ 27	9
+ 488	8
+ 510	5
+ 720	4
+ 235	1
+ 355	5
+ 528	5
+ 213	7
+ 711	9
+ 574	4
+ 122	1
+ 587	1
+ 876	9
+ 948	4
+ 723	8
+ 165	7
+ 764	7
+ 545	3
+ 134	3
+ 666	4
+ 321	0
+ 903	8
+ 489	1
+ 597	2
+ 23	2
+ 586	1
+ 259	2
+ 263	1
+ 50	2
+ 537	8
+ 60	7
+ 522	8
+ 354	1
+ 98	5
+ 331	8
+ 857	7
+ 786	8
+ 501	3
+ 876	1
+ 475	9
+ 269	1
+ 45	5
+ 234	3
+ 662	3
+ 518	2
+ 56	6
+ 900	6
+ 402	3
+ 644	5
+ 743	10
+ 264	6
+ 627	1
+ 360	1
+ 325	2
+ 226	8
+ 134	5
+ 861	2
+ 22	1
+ 486	7
+ 379	0
+ 882	4
+ 582	8
+ 12	10
+ 37	7
+ 484	8
+ 631	7
+ 379	3
+ 799	7
+ 387	1
+ 974	6
+ 925	1
+ 108	8
+ 287	1
+ 881	8
+ 813	3
+ 778	7
+ 695	4
+ 478	7
+ 344	5
+ 364	8
+ 294	10
+ 577	7
+ 254	4
+ 412	6
+ 500	4
+ 254	4
+ 495	4
+ 211	8
+ 491	1
+ 555	3
+ 352	3
+ 1000	0
+ 693	5
+ 755	0
+ 992	1
+ 865	3
+ 114	4
+ 959	4
+ 818	4
+ 9	3
+ 757	3
+ 743	3
+ 625	10
+ 34	1
+ 46	6
+ 421	4
+ 923	4
+ 445	6
+ 898	2
+ 653	9
+ 319	5
+ 175	4
+ 960	1
+ 802	8
+ 505	8
+ 96	3
+ 75	8
+ 515	7
+ 793	5
+ 817	8
+ 139	2
+ 236	1
+ 658	7
+ 677	6
+ 882	3
+ 445	2
+ 848	6
+ 635	8
+ 754	4
+ 586	3
+ 249	7
+ 523	3
+ 522	0
+ 24	3
+ 587	8
+ 153	7
+ 78	4
+ 787	7
+ 71	5
+ 291	10
+ 794	7
+ 155	6
+ 356	8
+ 451	1
+ 228	0
+ 370	5
+ 719	9
+ 801	2
+ 930	8
+ 556	5
+ 667	7
+ 242	7
+ 98	0
+ 481	2
+ 493	8
+ 123	3
+ 508	3
+ 929	9
+ 68	4
+ 974	3
+ 417	3
+ 772	1
+ 237	6
+ 378	2
+ 399	9
+ 683	1
+ 642	9
+ 811	7
+ 954	3
+ 910	4
+ 64	0
+ 734	6
+ 310	7
+ 437	4
+ 43	4
+ 674	5
+ 756	4
+ 596	10
+ 20	10
+ 158	4
+ 907	8
+ 485	5
+ 766	3
+ 290	7
+ 588	2
+ 167	7
+ 233	9
+ 224	5
+ 564	7
+ 922	6
+ 73	6
+ 67	8
+ 41	7
+ 820	1
+ 637	10
+ 480	5
+ 820	10
+ 94	6
+ 260	4
+ 306	8
+ 584	5
+ 500	8
+ 374	7
+ 361	9
+ 385	3
+ 545	5
+ 877	6
+ 286	9
+ 275	1
+ 979	9
+ 85	5
+ 457	9
+ 424	6
+ 492	7
+ 936	8
+ 531	5
+ 271	0
+ 337	6
+ 755	7
+ 583	1
+ 980	1
+ 599	9
+ 739	9
+ 776	0
+ 992	8
+ 926	1
+ 215	4
+ 982	6
+ 935	5
+ 322	9
+ 272	9
+ 391	5
+ 885	7
+ 189	6
+ 426	8
+ 780	4
+ 899	4
+ 264	6
+ 264	0
+ 652	3
+ 796	6
+ 332	0
+ 961	3
+ 649	9
+ 789	10
+ 767	1
+ 825	2
+ 605	7
+ 886	8
+ 349	3
+ 566	1
+ 719	5
+ 508	10
+ 103	8
+ 23	8
+ 28	8
+ 333	4
+ 830	3
+ 675	5
+ 190	5
+ 450	10
+ 525	3
+ 115	1
+ 984	0
+ 924	3
+ 313	5
+ 463	0
+ 955	10
+ 15	1
+ 743	0
+ 813	8
+ 858	1
+ 131	7
+ 440	8
+ 167	6
+ 270	6
+ 587	8
+ 892	7
+ 925	9
+ 702	8
+ 210	0
+ 339	7
+ 47	3
+ 643	1
+ 351	4
+ 101	2
+ 157	10
+ 310	3
+ 647	7
+ 93	8
+ 380	4
+ 432	10
+ 158	3
+ 668	1
+ 201	4
+ 933	4
+ 386	3
+ 83	4
+ 566	7
+ 496	9
+ 113	6
+ 81	3
+ 556	4
+ 557	2
+ 140	7
+ 16	5
+ 13	4
+ 487	2
+ 772	2
+ 253	10
+ 526	2
+ 384	9
+ 458	5
+ 345	0
+ 194	8
+ 941	3
+ 438	0
+ 577	10
+ 413	1
+ 196	6
+ 784	2
+ 74	8
+ 660	6
+ 967	4
+ 716	2
+ 405	2
+ 407	8
+ 154	9
+ 256	5
+ 888	4
+ 341	8
+ 757	8
+ 852	3
+ 771	3
+ 468	10
+ 819	3
+ 179	9
+ 49	8
+ 454	0
+ 271	2
+ 238	7
+ 413	6
+ 465	6
+ 509	7
+ 67	4
+ 171	4
+ 226	9
+ 186	1
+ 261	10
+ 343	7
+ 924	2
+ 982	1
+ 55	0
+ 942	5
+ 48	2
+ 679	3
+ 890	1
+ 930	4
+ 659	4
+ 75	7
+ 836	2
+ 133	1
+ 173	3
+ 141	4
+ 277	5
+ 164	2
+ 646	1
+ 305	7
+ 178	2
+ 210	2
+ 460	9
+ 512	4
+ 981	4
+ 705	6
+ 881	8
+ 366	7
+ 26	5
+ 780	2
+ 818	9
+ 634	1
+ 404	8
+ 296	0
+ 945	6
+ 751	1
+ 848	10
+ 349	3
+ 850	9
+ 658	8
+ 303	4
+ 471	2
+ 143	8
+ 902	2
+ 335	7
+ 368	2
+ 602	0
+ 248	0
+ 800	5
+ 55	7
+ 145	8
+ 868	10
+ 767	2
+ 301	6
+ 78	10
+ 447	4
+ 322	9
+ 566	5
+ 754	5
+ 633	1
+ 149	0
+ 242	8
+ 2	5
+ 757	8
+ 35	8
+ 547	2
+ 618	4
+ 174	4
+ 631	5
+ 1	7
+ 434	4
+ 91	8
+ 366	7
+ 221	1
+ 124	9
+ 208	3
+ 855	5
+ 25	9
+ 941	8
+ 660	10
+ 593	2
+ 157	2
+ 621	3
+ 596	3
+ 806	6
+ 962	2
+ 45	1
+ 996	4
+ 709	2
+ 530	8
+ 72	7
+ 107	9
+ 189	1
+ 784	1
+ 913	4
+ 106	5
+ 650	3
+ 717	3
+ 594	3
+ 524	4
+ 910	5
+ 640	10
+ 538	6
+ 365	2
+ 854	9
+ 80	9
+ 634	2
+ 852	8
+ 318	6
+ 953	2
+ 80	1
+ 737	7
+ 323	5
+ 2	9
+ 766	5
+ 317	7
+ 11	10
+ 630	5
+ 593	10
+ 795	4
+ 891	9
+ 372	5
+ 61	2
+ 348	4
+ 861	3
+ 610	9
+ 360	3
+ 672	7
+ 800	7
+ 599	6
+ 199	9
+ 242	2
+ 873	9
+ 759	5
+ 868	6
+ 912	8
+ 429	3
+ 284	5
+ 507	6
+ 869	4
+ 933	5
+ 309	3
+ 825	10
+ 976	6
+ 654	6
+ 190	9
+ 491	4
+ 63	4
+ 304	8
+ 829	2
+ 377	7
+ 931	8
+ 24	2
+ 295	5
+ 848	2
+ 899	8
+ 642	2
+ 74	5
+ 188	0
+ 92	8
+ 624	3
+ 695	1
+ 714	8
+ 479	0
+ 581	3
+ 191	10
+ 49	1
+ 763	1
+ 337	1
+ 604	2
+ 222	5
+ 965	9
+ 712	0
+ 332	9
+ 88	4
+ 742	7
+ 706	4
+ 828	4
+ 196	3
+ 438	8
+ 616	6
+ 735	7
+ 751	5
+ 738	1
+ 556	3
+ 272	8
+ 846	2
+ 643	6
+ 277	10
+ 457	4
+ 398	2
+ 77	1
+ 636	9
+ 524	8
+ 213	10
+ 609	8
+ 591	3
+ 493	3
+ 842	2
+ 430	4
+ 573	7
+ 177	4
+ 940	8
+ 977	2
+ 795	4
+ 581	2
+ 633	7
+ 297	3
+ 564	8
+ 101	8
+ 783	7
+ 605	4
+ 55	1
+ 716	9
+ 329	1
+ 296	9
+ 847	5
+ 321	8
+ 294	3
+ 3	1
+ 731	6
+ 281	4
+ 243	6
+ 634	8
+ 399	7
+ 583	2
+ 445	2
+ 555	5
+ 286	3
+ 398	6
+ 417	7
+ 517	3
+ 167	8
+ 51	5
+ 135	1
+ 549	9
+ 638	8
+ 231	9
+ 409	9
+ 687	8
+ 599	3
+ 989	0
+ 458	5
+ 545	7
+ 816	9
+ 359	2
+ 637	9
+ 496	8
+ 713	5
+ 265	8
+ 601	8
+ 715	2
+ 645	9
+ 119	1
+ 810	8
+ 862	4
+ 76	9
+ 454	5
+ 395	10
+ 279	2
+ 942	6
+ 442	6
+ 513	9
+ 383	2
+ 486	6
+ 73	1
+ 463	8
+ 325	1
+ 733	4
+ 162	5
+ 251	0
+ 952	3
+ 874	4
+ 862	3
+ 405	1
+ 479	3
+ 778	9
+ 925	3
+ 860	3
+ 516	3
+ 956	6
+ 433	4
+ 377	8
+ 527	1
+ 203	7
+ 654	5
+ 713	6
+ 781	6
+ 12	6
+ 856	4
+ 783	3
+ 763	6
+ 257	7
+ 852	1
+ 995	4
+ 463	10
+ 957	9
+ 369	3
+ 654	9
+ 445	9
+ 584	1
+ 310	3
+ 704	1
+ 884	7
+ 734	7
+ 132	5
+ 75	9
+ 79	3
+ 582	9
+ 449	6
+ 299	9
+ 527	3
+ 808	9
+ 590	5
+ 791	0
+ 318	4
+ 134	6
+ 671	8
+ 721	6
+ 554	5
+ 295	7
+ 973	4
+ 582	1
+ 702	2
+ 983	2
+ 741	3
+ 63	3
+ 538	9
+ 163	1
+ 333	10
+ 164	7
+ 329	3
+ 280	10
+ 136	0
+ 555	7
+ 455	8
+ 377	4
+ 220	10
+ 480	9
+ 122	5
+ 72	9
+ 744	1
+ 130	3
+ 6	3
+ 410	3
+ 248	6
+ 989	6
+ 872	3
+ 577	0
+ 270	1
+ 697	7
+ 981	1
+ 153	2
+ 32	6
+ 122	2
+ 96	2
+ 16	8
+ 329	1
+ 122	3
+ 440	5
+ 673	7
+ 107	7
+ 265	10
+ 932	8
+ 986	2
+ 972	7
+ 927	10
+ 757	1
+ 154	8
+ 713	3
+ 942	8
+ 470	10
+ 649	8
+ 104	8
+ 134	5
+ 305	8
+ 232	4
+ 469	5
+ 390	4
+ 338	4
+ 602	3
+ 58	5
+ 264	8
+ 609	4
+ 603	3
+ 694	5
+ 131	2
+ 503	8
+ 962	6
+ 552	1
+ 152	9
+ 902	4
+ 268	4
+ 881	7
+ 772	2
+ 33	4
+ 529	1
+ 903	8
+ 905	5
+ 210	5
+ 833	9
+ 53	10
+ 67	6
+ 744	0
+ 164	3
+ 125	3
+ 153	0
+ 699	4
+ 398	6
+ 78	2
+ 798	1
+ 544	3
+ 202	4
+ 119	1
+ 959	3
+ 615	8
+ 232	7
+ 756	3
+ 224	5
+ 328	4
+ 797	5
+ 703	10
+ 480	4
+ 371	9
+ 982	4
+ 49	8
+ 561	6
+ 106	8
+ 40	2
+ 869	10
+ 554	5
+ 790	8
+ 151	5
+ 85	4
+ 47	4
+ 763	8
+ 866	5
+ 794	3
+ 868	2
+ 225	8
+ 615	3
+ 629	2
+ 866	7
+ 937	9
+ 960	8
+ 904	5
+ 290	7
+ 301	4
+ 241	4
+ 816	3
+ 799	6
+ 131	7
+ 45	9
+ 12	9
+ 90	2
+ 762	7
+ 510	4
+ 880	4
+ 126	8
+ 282	1
+ 623	2
+ 601	9
+ 880	9
+ 354	1
+ 287	2
+ 408	1
+ 749	5
+ 753	8
+ 464	8
+ 707	6
+ 2	5
+ 258	5
+ 859	1
+ 888	10
+ 956	2
+ 71	6
+ 355	7
+ 492	2
+ 574	8
+ 355	9
+ 15	8
+ 948	8
+ 302	7
+ 558	8
+ 466	3
+ 320	5
+ 733	6
+ 980	6
+ 716	9
+ 577	7
+ 37	6
+ 251	4
+ 321	7
+ 627	9
+ 588	10
+ 756	6
+ 746	7
+ 367	0
+ 405	9
+ 814	9
+ 191	1
+ 338	9
+ 712	3
+ 517	4
+ 186	1
+ 100	2
+ 743	4
+ 615	1
+ 93	2
+ 958	7
+ 225	7
+ 284	10
+ 418	7
+ 19	8
+ 577	8
+ 693	8
+ 967	0
+ 692	7
+ 349	2
+ 106	5
+ 303	2
+ 758	0
+ 557	4
+ 109	7
+ 616	1
+ 332	8
+ 782	6
+ 812	2
+ 267	8
+ 22	8
+ 665	7
+ 612	6
+ 746	3
+ 309	1
+ 512	4
+ 630	8
+ 622	4
+ 860	2
+ 762	10
+ 830	4
+ 37	2
+ 219	8
+ 777	0
+ 19	0
+ 863	0
+ 888	5
+ 756	5
+ 159	5
+ 804	5
+ 597	3
+ 884	2
+ 131	5
+ 616	10
+ 685	4
+ 961	5
+ 756	10
+ 675	10
+ 818	5
+ 6	8
+ 496	9
+ 878	4
+ 397	6
+ 884	6
+ 135	7
+ 23	7
+ 3	9
+ 959	1
+ 412	6
+ 125	1
+ 953	1
+ 611	7
+ 84	3
+ 683	9
+ 739	7
+ 738	2
+ 559	6
+ 619	10
+ 249	5
+ 511	4
+ 190	5
+ 116	2
+ 442	1
+ 327	9
+ 649	5
+ 951	6
+ 538	6
+ 310	6
+ 848	10
+ 524	6
+ 684	3
+ 822	2
+ 878	4
+ 198	1
+ 943	7
+ 512	1
+ 244	6
+ 325	7
+ 702	7
+ 539	4
+ 104	5
+ 952	6
+ 52	3
+ 264	9
+ 257	8
+ 487	9
+ 50	3
+ 183	9
+ 748	4
+ 56	7
+ 91	6
+ 823	3
+ 195	1
+ 21	9
+ 801	6
+ 247	9
+ 50	2
+ 546	1
+ 462	8
+ 2	7
+ 597	5
+ 659	6
+ 797	8
+ 575	5
+ 224	6
+ 236	3
+ 198	1
+ 650	4
+ 208	7
+ 289	0
+ 231	5
+ 913	3
+ 735	5
+ 383	2
+ 268	4
+ 915	9
+ 874	6
+ 512	7
+ 417	1
+ 215	6
+ 718	5
+ 955	9
+ 511	6
+ 309	7
+ 275	6
+ 727	5
+ 133	6
+ 786	9
+ 99	2
+ 64	4
+ 554	10
+ 233	4
+ 554	7
+ 98	10
+ 832	3
+ 611	5
+ 765	6
+ 466	3
+ 170	8
+ 995	4
+ 371	7
+ 951	5
+ 363	7
+ 371	5
+ 907	4
+ 830	5
+ 414	1
+ 889	10
+ 808	10
+ 937	6
+ 301	5
+ 189	1
+ 114	7
+ 343	3
+ 429	3
+ 729	8
+ 61	7
+ 304	4
+ 416	7
+ 886	3
+ 110	7
+ 784	5
+ 779	7
+ 491	6
+ 660	4
+ 226	10
+ 976	4
+ 28	1
+ 71	4
+ 374	5
+ 709	1
+ 300	8
+ 782	6
+ 193	2
+ 280	1
+ 521	4
+ 794	3
+ 913	6
+ 978	4
+ 159	6
+ 833	4
+ 600	8
+ 801	6
+ 899	9
+ 999	3
+ 371	7
+ 376	7
+ 477	2
+ 276	7
+ 356	6
+ 749	9
+ 945	5
+ 183	9
+ 116	2
+ 262	3
+ 799	1
+ 661	4
+ 904	5
+ 28	8
+ 334	0
+ 76	7
+ 735	5
+ 376	2
+ 609	7
+ 882	10
+ 207	6
+ 843	2
+ 174	0
+ 10	3
+ 187	3
+ 565	10
+ 366	2
+ 386	3
+ 689	4
+ 73	0
+ 441	1
+ 727	2
+ 600	1
+ 388	2
+ 756	3
+ 176	10
+ 901	0
+ 115	1
+ 45	1
+ 364	2
+ 396	9
+ 218	8
+ 156	6
+ 32	8
+ 18	1
+ 867	5
+ 254	6
+ 635	9
+ 699	0
+ 65	5
+ 293	2
+ 417	2
+ 259	5
+ 268	3
+ 656	6
+ 535	1
+ 562	8
+ 814	7
+ 357	8
+ 563	4
+ 952	4
+ 834	2
+ 25	5
+ 60	7
+ 492	1
+ 178	8
+ 365	6
+ 977	6
+ 127	2
+ 928	8
+ 877	5
+ 834	4
+ 216	6
+ 157	6
+ 495	7
+ 949	4
+ 150	8
+ 653	2
+ 252	7
+ 898	7
+ 838	1
+ 527	2
+ 671	5
+ 827	8
+ 750	8
+ 581	6
+ 217	4
+ 66	4
+ 64	2
+ 7	6
+ 943	10
+ 6	1
+ 738	7
+ 267	10
+ 372	2
+ 733	2
+ 242	3
+ 413	9
+ 765	2
+ 712	5
+ 994	3
+ 142	2
+ 708	2
+ 645	8
+ 431	7
+ 331	4
+ 608	3
+ 466	3
+ 996	7
+ 336	4
+ 899	1
+ 577	1
+ 330	10
+ 54	1
+ 229	8
+ 609	2
+ 59	8
+ 435	8
+ 959	1
+ 539	4
+ 733	9
+ 763	3
+ 207	2
+ 687	2
+ 961	0
+ 570	9
+ 93	1
+ 1	4
+ 137	1
+ 517	4
+ 821	1
+ 590	9
+ 878	0
+ 646	8
+ 106	2
+ 226	8
+ 56	10
+ 180	3
+ 218	9
+ 466	2
+ 891	0
+ 39	10
+ 183	0
+ 407	3
+ 95	9
+ 686	9
+ 51	3
+ 795	9
+ 301	4
+ 765	4
+ 627	10
+ 246	7
+ 981	4
+ 946	2
+ 294	4
+ 378	2
+ 448	4
+ 170	6
+ 457	6
+ 950	6
+ 501	6
+ 467	6
+ 911	3
+ 480	2
+ 704	2
+ 619	3
+ 237	9
+ 14	2
+ 291	10
+ 416	6
+ 372	8
+ 770	8
+ 212	9
+ 451	7
+ 516	4
+ 221	0
+ 37	7
+ 568	9
+ 950	0
+ 160	7
+ 293	8
+ 985	5
+ 644	10
+ 747	9
+ 960	2
+ 519	3
+ 958	3
+ 151	2
+ 227	6
+ 838	7
+ 3	1
+ 760	0
+ 747	3
+ 988	7
+ 376	1
+ 351	7
+ 928	3
+ 198	6
+ 336	9
+ 506	3
+ 109	0
+ 627	1
+ 312	8
+ 236	5
+ 380	1
+ 283	4
+ 133	0
+ 423	9
+ 371	4
+ 577	7
+ 559	9
+ 415	5
+ 264	6
+ 58	6
+ 559	6
+ 896	7
+ 588	5
+ 734	9
+ 302	10
+ 440	7
+ 45	7
+ 66	2
+ 766	5
+ 58	1
+ 899	6
+ 883	5
+ 562	3
+ 945	8
+ 911	0
+ 427	5
+ 566	3
+ 138	2
+ 847	9
+ 55	1
+ 842	5
+ 832	9
+ 218	9
+ 66	10
+ 386	1
+ 120	3
+ 758	0
+ 743	3
+ 300	7
+ 147	2
+ 690	6
+ 681	3
+ 898	8
+ 411	7
+ 691	5
+ 895	5
+ 960	7
+ 420	2
+ 625	5
+ 162	0
+ 610	3
+ 296	4
+ 283	0
+ 688	6
+ 726	8
+ 794	4
+ 410	5
+ 673	3
+ 294	1
+ 54	10
+ 549	9
+ 518	5
+ 677	9
+ 687	3
+ 425	8
+ 314	0
+ 130	6
+ 402	4
+ 648	1
+ 997	4
+ 926	8
+ 791	3
+ 267	5
+ 645	6
+ 547	7
+ 546	1
+ 649	1
+ 605	3
+ 3	3
+ 628	4
+ 141	9
+ 462	3
+ 551	9
+ 684	2
+ 954	7
+ 574	9
+ 472	4
+ 217	7
+ 828	9
+ 299	4
+ 562	8
+ 471	2
+ 909	1
+ 536	9
+ 367	2
+ 339	5
+ 106	8
+ 779	7
+ 664	5
+ 856	6
+ 144	4
+ 499	6
+ 796	7
+ 353	6
+ 579	7
+ 999	1
+ 497	5
+ 351	4
+ 546	9
+ 317	9
+ 51	7
+ 421	2
+ 456	2
+ 813	1
+ 664	7
+ 738	8
+ 100	2
+ 422	9
+ 953	8
+ 520	5
+ 428	5
+ 672	9
+ 990	0
+ 331	5
+ 910	6
+ 448	10
+ 305	9
+ 118	8
+ 70	9
+ 881	7
+ 601	6
+ 541	7
+ 855	10
+ 597	8
+ 739	1
+ 341	2
+ 637	0
+ 93	6
+ 37	4
+ 162	9
+ 73	6
+ 908	4
+ 480	0
+ 139	6
+ 957	0
+ 284	6
+ 638	8
+ 259	5
+ 788	9
+ 302	5
+ 974	6
+ 695	6
+ 656	8
+ 237	7
+ 212	4
+ 639	3
+ 9	5
+ 663	5
+ 573	8
+ 39	5
+ 821	3
+ 88	5
+ 148	3
+ 952	9
+ 204	3
+ 464	2
+ 896	2
+ 789	6
+ 947	0
+ 244	2
+ 425	9
+ 444	4
+ 430	1
+ 924	0
+ 909	10
+ 533	7
+ 286	6
+ 189	4
+ 969	1
+ 370	2
+ 394	8
+ 350	3
+ 993	1
+ 842	9
+ 165	1
+ 99	6
+ 969	5
+ 24	4
+ 651	9
+ 401	6
+ 911	9
+ 290	2
+ 556	5
+ 631	5
+ 619	0
+ 696	0
+ 835	0
+ 303	8
+ 185	1
+ 767	3
+ 231	9
+ 940	2
+ 410	10
+ 598	1
+ 912	10
+ 621	8
+ 934	9
+ 20	5
+ 389	7
+ 14	0
+ 651	7
+ 22	5
+ 757	3
+ 313	9
+ 471	1
+ 292	7
+ 947	2
+ 902	4
+ 196	5
+ 418	1
+ 500	0
+ 931	4
+ 949	10
+ 924	3
+ 601	9
+ 348	3
+ 648	4
+ 738	4
+ 695	1
+ 347	2
+ 132	6
+ 867	1
+ 872	8
+ 436	1
+ 269	9
+ 176	8
+ 893	1
+ 203	8
+ 59	1
+ 181	7
+ 65	5
+ 912	7
+ 898	7
+ 118	6
+ 702	5
+ 758	8
+ 105	6
+ 913	10
+ 395	3
+ 45	7
+ 204	2
+ 433	1
+ 329	6
+ 939	4
+ 764	1
+ 48	8
+ 650	10
+ 542	5
+ 610	7
+ 141	3
+ 126	9
+ 146	2
+ 525	1
+ 208	9
+ 409	3
+ 584	6
+ 474	0
+ 710	8
+ 654	6
+ 190	4
+ 770	2
+ 247	4
+ 198	8
+ 968	8
+ 448	1
+ 121	6
+ 8	3
+ 805	5
+ 326	0
+ 452	7
+ 265	0
+ 347	7
+ 53	1
+ 542	7
+ 706	7
+ 124	5
+ 970	4
+ 896	2
+ 159	9
+ 977	6
+ 972	1
+ 182	10
+ 364	10
+ 513	7
+ 999	10
+ 425	3
+ 1000	8
+ 3	1
+ 829	5
+ 759	5
+ 277	9
+ 12	2
+ 254	9
+ 415	4
+ 772	4
+ 21	7
+ 490	2
+ 725	9
+ 189	2
+ 544	2
+ 202	10
+ 452	2
+ 741	5
+ 254	6
+ 1000	0
+ 106	3
+ 896	1
+ 523	1
+ 27	9
+ 563	8
+ 330	6
+ 544	8
+ 786	3
+ 674	10
+ 506	2
+ 162	7
+ 186	6
+ 910	9
+ 69	2
+ 496	1
+ 177	6
+ 346	1
+ 720	9
+ 223	7
+ 807	8
+ 546	1
+ 369	1
+ 958	2
+ 358	6
+ 129	9
+ 849	3
+ 573	0
+ 906	5
+ 961	10
+ 646	5
+ 45	8
+ 59	4
+ 896	8
+ 259	1
+ 526	1
+ 904	1
+ 204	3
+ 162	2
+ 428	5
+ 793	6
+ 385	6
+ 849	10
+ 676	8
+ 440	6
+ 731	1
+ 94	8
+ 909	2
+ 166	8
+ 933	4
+ 923	5
+ 492	8
+ 531	7
+ 100	7
+ 858	5
+ 214	7
+ 86	6
+ 292	9
+ 556	10
+ 691	10
+ 604	4
+ 82	7
+ 197	10
+ 851	4
+ 796	8
+ 788	7
+ 243	3
+ 547	8
+ 975	6
+ 467	8
+ 176	7
+ 484	3
+ 279	8
+ 198	8
+ 743	9
+ 832	3
+ 310	9
+ 46	5
+ 906	9
+ 871	7
+ 681	7
+ 422	9
+ 938	10
+ 698	9
+ 615	2
+ 747	8
+ 846	2
+ 53	1
+ 6	3
+ 961	7
+ 139	8
+ 97	4
+ 707	1
+ 957	6
+ 40	8
+ 314	7
+ 487	7
+ 645	4
+ 704	3
+ 339	3
+ 508	1
+ 110	4
+ 315	2
+ 479	3
+ 414	4
+ 70	6
+ 231	2
+ 3	9
+ 311	10
+ 550	4
+ 788	9
+ 72	3
+ 600	7
+ 700	3
+ 60	0
+ 623	6
+ 124	7
+ 922	4
+ 897	4
+ 760	3
+ 839	8
+ 864	1
+ 998	9
+ 9	3
+ 827	6
+ 660	6
+ 423	7
+ 891	0
+ 450	6
+ 327	5
+ 630	10
+ 78	8
+ 685	0
+ 194	6
+ 401	10
+ 893	2
+ 785	8
+ 311	8
+ 625	3
+ 92	5
+ 878	8
+ 68	3
+ 484	10
+ 325	9
+ 550	7
+ 444	2
+ 603	5
+ 935	3
+ 522	1
+ 870	9
+ 82	8
+ 163	9
+ 521	5
+ 650	1
+ 794	7
+ 598	7
+ 494	7
+ 974	10
+ 625	3
+ 911	2
+ 951	4
+ 356	6
+ 877	3
+ 842	4
+ 419	7
+ 322	5
+ 476	5
+ 369	10
+ 960	0
+ 143	8
+ 761	7
+ 426	3
+ 408	4
+ 233	0
+ 698	1
+ 209	6
+ 499	6
+ 203	4
+ 856	0
+ 775	3
+ 757	1
+ 776	2
+ 583	1
+ 229	5
+ 164	4
+ 297	9
+ 114	7
+ 180	5
+ 122	4
+ 555	8
+ 556	8
+ 469	1
+ 328	7
+ 431	2
+ 717	2
+ 459	5
+ 302	2
+ 706	9
+ 380	9
+ 428	5
+ 308	7
+ 468	4
+ 447	6
+ 944	6
+ 60	5
+ 390	6
+ 262	9
+ 672	6
+ 531	1
+ 774	2
+ 307	2
+ 721	6
+ 468	4
+ 495	8
+ 363	9
+ 392	7
+ 648	9
+ 93	1
+ 508	0
+ 664	6
+ 536	1
+ 185	8
+ 913	9
+ 390	4
+ 959	2
+ 692	3
+ 397	4
+ 877	9
+ 841	4
+ 713	2
+ 295	1
+ 875	9
+ 965	10
+ 37	5
+ 6	7
+ 42	5
+ 755	2
+ 342	7
+ 84	7
+ 112	0
+ 895	8
+ 310	3
+ 218	2
+ 158	1
+ 559	9
+ 264	9
+ 976	1
+ 796	9
+ 108	8
+ 413	1
+ 533	5
+ 658	3
+ 682	10
+ 956	8
+ 731	1
+ 809	6
+ 873	1
+ 918	1
+ 307	1
+ 152	9
+ 947	4
+ 719	9
+ 555	5
+ 863	7
+ 347	3
+ 778	9
+ 731	4
+ 168	4
+ 435	1
+ 179	2
+ 193	10
+ 791	1
+ 108	7
+ 159	4
+ 786	3
+ 280	7
+ 726	10
+ 655	3
+ 512	5
+ 944	9
+ 793	7
+ 739	5
+ 158	9
+ 937	6
+ 32	1
+ 758	2
+ 104	5
+ 292	2
+ 259	5
+ 626	0
+ 761	9
+ 777	5
+ 903	4
+ 767	4
+ 949	7
+ 274	7
+ 434	0
+ 266	6
+ 921	2
+ 184	10
+ 318	9
+ 178	4
+ 491	5
+ 633	8
+ 921	3
+ 795	7
+ 164	6
+ 168	1
+ 3	9
+ 483	10
+ 647	8
+ 694	1
+ 771	10
+ 673	7
+ 163	9
+ 644	5
+ 799	8
+ 903	3
+ 292	5
+ 40	2
+ 794	8
+ 895	10
+ 407	1
+ 25	4
+ 999	5
+ 362	6
+ 265	1
+ 727	0
+ 16	4
+ 727	2
+ 257	4
+ 660	1
+ 193	6
+ 345	5
+ 98	4
+ 698	9
+ 221	6
+ 850	6
+ 656	9
+ 37	7
+ 383	4
+ 301	6
+ 455	0
+ 684	5
+ 428	4
+ 650	7
+ 781	3
+ 740	10
+ 872	1
+ 459	10
+ 471	2
+ 863	7
+ 749	7
+ 319	4
+ 589	4
+ 59	10
+ 755	4
+ 621	2
+ 388	3
+ 681	8
+ 716	3
+ 501	5
+ 641	2
+ 471	5
+ 327	9
+ 484	8
+ 87	3
+ 490	8
+ 60	8
+ 241	6
+ 166	3
+ 622	9
+ 661	2
+ 132	0
+ 547	8
+ 865	3
+ 144	4
+ 760	8
+ 606	2
+ 299	9
+ 162	8
+ 731	2
+ 130	2
+ 85	2
+ 30	3
+ 840	2
+ 627	5
+ 117	3
+ 705	2
+ 337	3
+ 61	2
+ 515	2
+ 566	3
+ 991	2
+ 506	3
+ 105	7
+ 74	8
+ 916	2
+ 57	0
+ 395	1
+ 328	2
+ 282	10
+ 697	4
+ 243	4
+ 647	6
+ 654	7
+ 781	2
+ 914	3
+ 444	9
+ 520	9
+ 196	6
+ 618	3
+ 461	5
+ 474	5
+ 535	9
+ 604	9
+ 105	9
+ 818	8
+ 285	1
+ 205	9
+ 641	9
+ 641	4
+ 28	6
+ 768	5
+ 461	3
+ 421	7
+ 913	0
+ 927	4
+ 573	4
+ 892	1
+ 271	5
+ 971	4
+ 382	8
+ 179	7
+ 851	4
+ 600	5
+ 243	2
+ 913	3
+ 796	7
+ 742	3
+ 969	2
+ 914	9
+ 202	8
+ 257	8
+ 243	1
+ 882	5
+ 644	9
+ 891	0
+ 643	1
+ 694	5
+ 454	3
+ 986	7
+ 535	9
+ 967	3
+ 580	7
+ 588	5
+ 871	5
+ 432	1
+ 344	7
+ 847	6
+ 837	7
+ 100	5
+ 583	10
+ 508	2
+ 61	2
+ 721	5
+ 497	7
+ 211	0
+ 606	2
+ 363	2
+ 887	10
+ 736	8
+ 454	2
+ 831	8
+ 858	7
+ 384	7
+ 408	5
+ 176	10
+ 475	7
+ 218	5
+ 887	9
+ 51	4
+ 646	3
+ 415	3
+ 440	8
+ 438	3
+ 729	2
+ 86	2
+ 344	9
+ 981	2
+ 596	4
+ 896	0
+ 850	1
+ 995	3
+ 756	2
+ 861	6
+ 152	9
+ 26	8
+ 174	4
+ 50	6
+ 218	5
+ 942	9
+ 663	0
+ 131	0
+ 944	1
+ 208	5
+ 477	1
+ 544	3
+ 176	5
+ 652	9
+ 752	5
+ 574	9
+ 424	6
+ 702	6
+ 41	8
+ 212	3
+ 241	2
+ 207	9
+ 181	3
+ 911	1
+ 450	1
+ 665	9
+ 222	2
+ 254	4
+ 748	9
+ 329	5
+ 418	9
+ 405	8
+ 504	1
+ 441	5
+ 860	7
+ 803	1
+ 807	0
+ 4	10
+ 348	9
+ 114	8
+ 33	8
+ 725	3
+ 988	10
+ 653	7
+ 885	10
+ 238	3
+ 886	6
+ 146	4
+ 751	6
+ 934	6
+ 240	7
+ 712	0
+ 748	7
+ 35	1
+ 631	1
+ 894	7
+ 928	6
+ 920	9
+ 598	6
+ 654	5
+ 556	9
+ 786	4
+ 535	9
+ 832	3
+ 518	8
+ 896	8
+ 504	6
+ 804	3
+ 324	8
+ 347	10
+ 989	2
+ 619	9
+ 860	5
+ 834	5
+ 113	5
+ 942	7
+ 380	7
+ 112	9
+ 659	9
+ 200	2
+ 711	1
+ 934	2
+ 704	7
+ 466	0
+ 578	8
+ 983	6
+ 55	6
+ 485	9
+ 142	3
+ 373	3
+ 808	3
+ 925	2
+ 43	0
+ 102	7
+ 981	3
+ 878	7
+ 398	8
+ 906	1
+ 551	4
+ 129	1
+ 186	1
+ 697	2
+ 715	2
+ 156	9
+ 502	5
+ 112	3
+ 844	0
+ 497	9
+ 74	6
+ 589	1
+ 900	5
+ 747	3
+ 280	7
+ 399	8
+ 26	5
+ 961	2
+ 641	7
+ 453	4
+ 840	6
+ 212	3
+ 138	3
+ 651	10
+ 362	1
+ 869	4
+ 746	5
+ 490	6
+ 925	2
+ 943	2
+ 890	3
+ 36	9
+ 870	10
+ 128	5
+ 655	6
+ 866	5
+ 190	1
+ 837	3
+ 403	5
+ 310	8
+ 636	2
+ 200	4
+ 637	7
+ 28	6
+ 927	10
+ 766	8
+ 313	8
+ 733	2
+ 798	9
+ 695	5
+ 443	6
+ 948	6
+ 640	8
+ 960	0
+ 274	3
+ 808	9
+ 449	0
+ 292	1
+ 698	3
+ 648	6
+ 291	4
+ 443	6
+ 215	2
+ 788	0
+ 37	5
+ 467	5
+ 44	4
+ 112	7
+ 200	1
+ 727	5
+ 342	5
+ 383	8
+ 542	7
+ 877	2
+ 995	5
+ 866	3
+ 938	3
+ 891	2
+ 484	7
+ 167	5
+ 162	6
+ 1	2
+ 48	1
+ 890	2
+ 186	6
+ 721	5
+ 151	1
+ 318	7
+ 779	2
+ 934	8
+ 719	8
+ 61	7
+ 108	10
+ 810	6
+ 632	10
+ 114	8
+ 610	1
+ 0	7
+ 229	9
+ 906	4
+ 506	6
+ 942	7
+ 731	3
+ 350	5
+ 455	3
+ 284	2
+ 83	3
+ 830	2
+ 297	6
+ 783	9
+ 617	9
+ 723	2
+ 12	7
+ 885	2
+ 614	8
+ 656	1
+ 418	6
+ 777	1
+ 858	1
+ 659	3
+ 411	9
+ 486	5
+ 288	3
+ 685	6
+ 957	5
+ 514	6
+ 365	2
+ 801	4
+ 961	7
+ 618	6
+ 477	3
+ 695	9
+ 871	5
+ 44	7
+ 600	7
+ 42	0
+ 646	5
+ 504	9
+ 845	2
+ 520	8
+ 657	0
+ 375	0
+ 272	2
+ 398	2
+ 862	0
+ 809	3
+ 290	5
+ 234	2
+ 976	3
+ 891	6
+ 982	9
+ 587	6
+ 461	1
+ 563	3
+ 280	1
+ 107	9
+ 117	5
+ 958	4
+ 658	4
+ 623	5
+ 372	4
+ 859	7
+ 935	1
+ 823	9
+ 372	7
+ 488	4
+ 646	1
+ 982	1
+ 165	5
+ 412	4
+ 628	5
+ 382	7
+ 2	3
+ 135	7
+ 696	8
+ 179	1
+ 190	0
+ 730	1
+ 131	6
+ 36	5
+ 266	5
+ 857	9
+ 598	8
+ 19	8
+ 384	4
+ 209	0
+ 951	6
+ 759	10
+ 932	9
+ 612	6
+ 652	8
+ 696	8
+ 830	4
+ 966	10
+ 978	0
+ 464	2
+ 527	3
+ 155	1
+ 160	2
+ 889	5
+ 605	1
+ 556	6
+ 690	3
+ 508	6
+ 209	1
+ 249	9
+ 912	9
+ 703	7
+ 370	7
+ 703	3
+ 672	2
+ 591	2
+ 488	7
+ 324	6
+ 921	2
+ 191	5
+ 311	7
+ 82	0
+ 62	6
+ 621	3
+ 710	9
+ 132	6
+ 815	8
+ 364	2
+ 504	1
+ 533	2
+ 234	1
+ 374	7
+ 872	7
+ 369	8
+ 911	6
+ 319	2
+ 307	4
+ 221	4
+ 990	8
+ 641	7
+ 713	8
+ 323	5
+ 608	7
+ 714	1
+ 755	2
+ 288	10
+ 372	7
+ 711	2
+ 360	1
+ 36	3
+ 640	4
+ 492	9
+ 755	7
+ 317	7
+ 556	10
+ 446	3
+ 731	8
+ 798	3
+ 457	5
+ 450	2
+ 760	7
+ 201	1
+ 400	9
+ 375	8
+ 991	4
+ 31	6
+ 765	5
+ 578	5
+ 237	9
+ 265	8
+ 852	7
+ 63	6
+ 481	9
+ 921	9
+ 374	4
+ 149	1
+ 109	3
+ 265	5
+ 261	6
+ 270	3
+ 50	3
+ 883	8
+ 824	5
+ 335	1
+ 355	6
+ 854	2
+ 312	9
+ 789	8
+ 778	7
+ 730	2
+ 81	9
+ 285	2
+ 228	6
+ 700	5
+ 190	10
+ 740	2
+ 271	6
+ 55	1
+ 84	4
+ 156	4
+ 990	0
+ 646	3
+ 927	4
+ 94	7
+ 145	8
+ 856	2
+ 702	1
+ 417	9
+ 692	1
+ 418	9
+ 87	2
+ 122	4
+ 782	2
+ 453	9
+ 567	6
+ 305	6
+ 619	10
+ 859	5
+ 386	10
+ 250	5
+ 776	1
+ 757	5
+ 249	2
+ 407	9
+ 291	8
+ 823	4
+ 983	9
+ 736	8
+ 121	2
+ 631	7
+ 798	9
+ 245	4
+ 886	1
+ 963	3
+ 57	2
+ 803	8
+ 320	6
+ 310	6
+ 734	7
+ 509	0
+ 543	3
+ 403	5
+ 276	1
+ 291	4
+ 328	9
+ 85	1
+ 858	3
+ 544	7
+ 434	5
+ 16	5
+ 719	8
+ 324	0
+ 378	6
+ 607	1
+ 352	1
+ 137	9
+ 447	5
+ 420	7
+ 679	7
+ 119	0
+ 634	2
+ 134	5
+ 535	7
+ 236	10
+ 184	3
+ 461	9
+ 71	8
+ 942	4
+ 418	5
+ 561	8
+ 664	7
+ 664	1
+ 238	1
+ 834	9
+ 796	10
+ 924	4
+ 158	1
+ 921	7
+ 735	2
+ 662	9
+ 409	1
+ 822	5
+ 907	8
+ 929	3
+ 312	5
+ 95	10
+ 188	8
+ 87	4
+ 844	9
+ 342	6
+ 874	3
+ 69	0
+ 324	10
+ 724	1
+ 148	4
+ 977	6
+ 510	8
+ 38	4
+ 563	10
+ 743	9
+ 458	8
+ 851	6
+ 598	9
+ 72	4
+ 859	4
+ 81	7
+ 680	2
+ 764	0
+ 141	5
+ 63	3
+ 875	0
+ 846	4
+ 839	9
+ 801	4
+ 851	5
+ 277	3
+ 382	1
+ 955	10
+ 65	0
+ 421	9
+ 441	5
+ 656	1
+ 653	4
+ 125	8
+ 908	2
+ 83	8
+ 228	9
+ 167	1
+ 813	10
+ 469	7
+ 513	7
+ 974	9
+ 873	9
+ 875	9
+ 955	3
+ 862	4
+ 799	5
+ 517	5
+ 939	6
+ 245	8
+ 830	3
+ 630	1
+ 257	8
+ 126	1
+ 765	6
+ 734	3
+ 341	7
+ 173	2
+ 637	0
+ 152	6
+ 344	0
+ 988	1
+ 533	5
+ 594	5
+ 148	8
+ 319	10
+ 166	9
+ 37	4
+ 745	2
+ 493	5
+ 757	2
+ 788	1
+ 935	10
+ 311	6
+ 9	5
+ 164	4
+ 478	2
+ 495	0
+ 658	1
+ 483	8
+ 927	8
+ 785	1
+ 751	8
+ 516	5
+ 984	0
+ 7	7
+ 235	8
+ 840	2
+ 756	2
+ 742	8
+ 615	9
+ 118	1
+ 58	6
+ 104	7
+ 700	6
+ 522	6
+ 389	3
+ 720	1
+ 128	2
+ 637	1
+ 244	6
+ 854	5
+ 439	7
+ 650	2
+ 845	4
+ 961	5
+ 298	1
+ 552	4
+ 690	7
+ 72	4
+ 243	6
+ 18	6
+ 901	7
+ 772	0
+ 973	4
+ 142	2
+ 52	10
+ 695	5
+ 691	3
+ 687	5
+ 737	6
+ 995	0
+ 725	5
+ 392	4
+ 203	5
+ 806	4
+ 59	8
+ 77	10
+ 562	8
+ 989	5
+ 258	1
+ 751	3
+ 127	4
+ 802	8
+ 792	5
+ 353	5
+ 136	3
+ 564	9
+ 895	10
+ 278	1
+ 420	1
+ 544	5
+ 908	6
+ 438	5
+ 471	4
+ 5	7
+ 558	8
+ 40	7
+ 203	8
+ 503	10
+ 331	9
+ 523	5
+ 205	1
+ 330	1
+ 42	6
+ 199	5
+ 692	7
+ 941	6
+ 363	4
+ 70	8
+ 806	1
+ 563	4
+ 831	6
+ 49	0
+ 445	6
+ 28	8
+ 408	6
+ 245	6
+ 638	6
+ 713	7
+ 182	9
+ 142	9
+ 654	1
+ 473	0
+ 463	5
+ 852	3
+ 619	4
+ 632	4
+ 18	7
+ 484	5
+ 233	5
+ 240	6
+ 63	5
+ 254	7
+ 59	10
+ 380	2
+ 880	5
+ 114	5
+ 606	6
+ 551	1
+ 131	4
+ 337	7
+ 818	10
+ 199	8
+ 650	7
+ 299	9
+ 195	5
+ 524	3
+ 23	8
+ 958	1
+ 746	3
+ 322	6
+ 860	4
+ 159	5
+ 23	7
+ 535	2
+ 114	9
+ 904	9
+ 842	1
+ 767	5
+ 786	1
+ 375	10
+ 604	9
+ 239	6
+ 678	2
+ 708	4
+ 534	0
+ 49	4
+ 466	2
+ 861	5
+ 919	4
+ 643	0
+ 269	5
+ 964	1
+ 650	7
+ 603	4
+ 797	10
+ 418	4
+ 877	7
+ 28	6
+ 853	7
+ 979	4
+ 766	0
+ 782	2
+ 236	6
+ 721	2
+ 40	4
+ 188	3
+ 911	2
+ 419	6
+ 884	0
+ 999	7
+ 1000	4
+ 82	9
+ 73	1
+ 432	9
+ 846	4
+ 313	6
+ 439	1
+ 844	7
+ 739	6
+ 831	8
+ 929	0
+ 87	8
+ 172	5
+ 402	1
+ 528	4
+ 736	5
+ 817	8
+ 405	9
+ 927	8
+ 817	8
+ 249	1
+ 384	7
+ 225	2
+ 364	10
+ 793	2
+ 742	7
+ 215	8
+ 562	4
+ 336	10
+ 443	9
+ 365	2
+ 392	2
+ 997	8
+ 72	9
+ 635	9
+ 697	9
+ 19	1
+ 573	2
+ 309	9
+ 208	1
+ 132	10
+ 824	3
+ 780	4
+ 734	1
+ 351	2
+ 980	7
+ 356	4
+ 897	4
+ 170	10
+ 276	8
+ 858	10
+ 690	9
+ 54	3
+ 121	4
+ 199	3
+ 466	3
+ 280	3
+ 678	1
+ 677	4
+ 175	0
+ 589	2
+ 743	9
+ 527	6
+ 297	7
+ 610	6
+ 502	5
+ 547	2
+ 345	6
+ 454	5
+ 965	7
+ 795	4
+ 983	1
+ 721	7
+ 135	4
+ 74	3
+ 425	7
+ 465	2
+ 607	10
+ 808	9
+ 689	4
+ 478	2
+ 886	0
+ 382	2
+ 626	8
+ 697	6
+ 488	5
+ 21	5
+ 567	7
+ 133	7
+ 140	2
+ 12	6
+ 869	5
+ 734	5
+ 469	5
+ 381	2
+ 960	9
+ 349	8
+ 884	7
+ 77	5
+ 567	8
+ 100	1
+ 266	1
+ 527	8
+ 864	7
+ 535	0
+ 867	5
+ 570	7
+ 24	3
+ 213	5
+ 845	6
+ 651	8
+ 453	0
+ 651	3
+ 732	7
+ 846	3
+ 501	9
+ 355	8
+ 67	9
+ 600	9
+ 542	1
+ 935	4
+ 682	5
+ 146	7
+ 808	4
+ 199	7
+ 953	9
+ 459	4
+ 851	1
+ 743	6
+ 837	6
+ 882	3
+ 534	2
+ 105	6
+ 118	7
+ 532	7
+ 840	5
+ 70	5
+ 971	2
+ 228	8
+ 575	4
+ 433	5
+ 277	9
+ 935	1
+ 1	7
+ 710	8
+ 266	6
+ 176	8
+ 828	3
+ 402	9
+ 986	9
+ 607	8
+ 399	7
+ 348	4
+ 892	6
+ 150	5
+ 1	6
+ 996	3
+ 474	9
+ 406	5
+ 609	1
+ 312	9
+ 708	5
+ 676	5
+ 768	1
+ 483	8
+ 10	1
+ 580	4
+ 766	9
+ 780	7
+ 502	9
+ 125	5
+ 513	1
+ 782	10
+ 52	2
+ 461	7
+ 304	8
+ 535	0
+ 261	2
+ 548	0
+ 288	0
+ 783	3
+ 121	4
+ 708	9
+ 290	5
+ 545	8
+ 418	7
+ 296	9
+ 791	1
+ 918	8
+ 266	4
+ 504	6
+ 153	0
+ 581	4
+ 250	1
+ 443	5
+ 161	2
+ 836	3
+ 589	5
+ 169	9
+ 32	7
+ 671	4
+ 384	10
+ 381	2
+ 45	3
+ 19	3
+ 678	5
+ 881	8
+ 561	5
+ 244	8
+ 591	7
+ 349	8
+ 913	2
+ 34	5
+ 728	2
+ 380	8
+ 916	1
+ 209	3
+ 18	6
+ 476	1
+ 890	5
+ 374	6
+ 17	3
+ 399	6
+ 716	6
+ 389	3
+ 330	7
+ 60	2
+ 922	1
+ 744	6
+ 296	1
+ 409	2
+ 174	6
+ 513	2
+ 209	10
+ 255	1
+ 483	6
+ 667	5
+ 883	1
+ 78	6
+ 708	5
+ 907	0
+ 204	10
+ 280	1
+ 60	0
+ 775	4
+ 147	2
+ 569	3
+ 803	1
+ 512	0
+ 71	8
+ 111	6
+ 396	8
+ 53	3
+ 842	1
+ 878	6
+ 597	8
+ 588	8
+ 751	9
+ 927	8
+ 891	7
+ 169	0
+ 886	7
+ 359	7
+ 820	9
+ 701	9
+ 638	8
+ 445	0
+ 588	5
+ 312	4
+ 628	2
+ 981	2
+ 975	6
+ 26	7
+ 437	10
+ 538	3
+ 655	7
+ 366	5
+ 445	7
+ 229	3
+ 595	9
+ 156	2
+ 741	6
+ 266	3
+ 98	6
+ 760	7
+ 768	7
+ 952	7
+ 310	10
+ 469	7
+ 931	0
+ 74	6
+ 713	4
+ 127	2
+ 164	4
+ 423	8
+ 286	6
+ 992	0
+ 180	3
+ 357	3
+ 837	1
+ 5	6
+ 858	10
+ 348	2
+ 935	8
+ 915	9
+ 823	10
+ 452	5
+ 429	6
+ 694	6
+ 935	1
+ 352	2
+ 696	3
+ 249	9
+ 602	6
+ 153	4
+ 722	2
+ 44	6
+ 115	4
+ 748	0
+ 208	7
+ 915	0
+ 652	4
+ 566	1
+ 946	3
+ 673	9
+ 377	0
+ 102	1
+ 369	4
+ 948	10
+ 956	1
+ 409	7
+ 259	5
+ 258	4
+ 844	0
+ 423	1
+ 669	3
+ 82	3
+ 705	6
+ 402	7
+ 908	1
+ 533	3
+ 101	6
+ 357	5
+ 986	3
+ 440	9
+ 406	8
+ 620	7
+ 303	9
+ 39	1
+ 885	5
+ 199	6
+ 801	3
+ 875	5
+ 929	3
+ 157	8
+ 353	7
+ 123	5
+ 325	5
+ 923	3
+ 785	4
+ 252	2
+ 213	9
+ 857	5
+ 751	9
+ 663	6
+ 359	9
+ 190	2
+ 142	1
+ 665	1
+ 343	8
+ 909	7
+ 513	0
+ 149	8
+ 513	1
+ 148	3
+ 435	4
+ 489	6
+ 273	3
+ 163	0
+ 243	8
+ 660	6
+ 687	8
+ 761	8
+ 914	4
+ 901	3
+ 249	8
+ 952	8
+ 843	1
+ 600	4
+ 173	7
+ 653	6
+ 149	1
+ 255	4
+ 489	4
+ 446	7
+ 244	1
+ 334	9
+ 955	1
+ 760	9
+ 521	7
+ 126	8
+ 471	1
+ 532	3
+ 180	1
+ 668	4
+ 880	3
+ 961	0
+ 464	2
+ 450	10
+ 634	9
+ 685	9
+ 2	0
+ 809	10
+ 113	6
+ 826	6
+ 230	10
+ 405	7
+ 30	9
+ 14	2
+ 69	7
+ 563	9
+ 3	5
+ 978	5
+ 740	4
+ 420	4
+ 324	1
+ 252	3
+ 123	1
+ 283	2
+ 631	1
+ 871	9
+ 60	3
+ 561	1
+ 213	6
+ 301	3
+ 257	9
+ 232	3
+ 388	2
+ 727	1
+ 637	1
+ 501	10
+ 252	8
+ 288	4
+ 815	6
+ 612	4
+ 678	5
+ 306	7
+ 759	9
+ 829	10
+ 442	1
+ 255	7
+ 994	5
+ 959	4
+ 696	7
+ 509	3
+ 833	0
+ 294	1
+ 764	6
+ 461	6
+ 152	1
+ 25	8
+ 555	3
+ 569	3
+ 199	4
+ 287	6
+ 528	5
+ 339	5
+ 28	3
+ 903	7
+ 983	4
+ 57	8
+ 422	4
+ 902	2
+ 933	4
+ 765	1
+ 435	8
+ 915	10
+ 122	5
+ 304	3
+ 882	6
+ 961	4
+ 133	3
+ 931	2
+ 598	8
+ 885	6
+ 246	9
+ 397	7
+ 292	3
+ 853	2
+ 662	6
+ 310	1
+ 409	2
+ 86	5
+ 709	4
+ 852	6
+ 982	8
+ 1	1
+ 114	9
+ 276	7
+ 766	2
+ 293	0
+ 102	7
+ 680	4
+ 989	5
+ 620	7
+ 152	9
+ 747	6
+ 154	8
+ 92	9
+ 224	9
+ 454	2
+ 758	5
+ 321	9
+ 386	6
+ 584	2
+ 758	9
+ 164	9
+ 567	8
+ 255	6
+ 377	9
+ 207	5
+ 804	10
+ 89	10
+ 788	2
+ 821	0
+ 126	3
+ 218	9
+ 729	5
+ 757	1
+ 136	3
+ 267	9
+ 219	4
+ 755	8
+ 275	0
+ 342	7
+ 885	5
+ 179	7
+ 503	3
+ 648	3
+ 450	5
+ 303	6
+ 743	5
+ 460	5
+ 60	2
+ 587	2
+ 559	9
+ 91	8
+ 285	8
+ 563	6
+ 856	9
+ 211	7
+ 454	4
+ 430	10
+ 659	1
+ 249	1
+ 546	6
+ 685	3
+ 72	1
+ 762	1
+ 363	3
+ 328	9
+ 202	4
+ 699	5
+ 265	3
+ 47	1
+ 168	3
+ 862	6
+ 649	3
+ 580	3
+ 369	8
+ 417	9
+ 379	1
+ 205	5
+ 247	10
+ 583	6
+ 315	9
+ 532	5
+ 331	2
+ 5	6
+ 493	1
+ 717	7
+ 310	6
+ 283	10
+ 870	9
+ 267	2
+ 691	7
+ 154	1
+ 786	4
+ 522	0
+ 326	1
+ 642	6
+ 17	2
+ 158	3
+ 405	2
+ 943	9
+ 215	7
+ 559	5
+ 238	8
+ 484	1
+ 704	8
+ 346	4
+ 435	5
+ 465	2
+ 860	10
+ 253	2
+ 92	9
+ 826	1
+ 70	10
+ 456	5
+ 147	4
+ 373	4
+ 60	9
+ 887	3
+ 774	4
+ 405	5
+ 122	8
+ 873	6
+ 253	3
+ 778	1
+ 326	0
+ 298	4
+ 927	1
+ 527	10
+ 109	10
+ 471	3
+ 383	8
+ 618	4
+ 775	5
+ 740	5
+ 875	1
+ 27	10
+ 897	9
+ 554	1
+ 239	3
+ 263	6
+ 362	6
+ 982	3
+ 686	5
+ 285	8
+ 492	8
+ 51	9
+ 600	7
+ 317	4
+ 173	1
+ 924	0
+ 203	10
+ 45	1
+ 851	6
+ 250	1
+ 930	5
+ 654	3
+ 74	6
+ 581	8
+ 145	9
+ 554	6
+ 623	6
+ 511	2
+ 274	8
+ 598	4
+ 886	5
+ 496	1
+ 474	5
+ 189	3
+ 141	4
+ 414	1
+ 953	1
+ 363	0
+ 704	9
+ 786	8
+ 811	3
+ 485	4
+ 946	10
+ 657	2
+ 825	3
+ 668	7
+ 778	2
+ 800	3
+ 705	10
+ 576	8
+ 429	10
+ 916	4
+ 58	3
+ 409	8
+ 225	2
+ 610	0
+ 536	1
+ 470	5
+ 92	1
+ 702	9
+ 383	4
+ 628	2
+ 533	4
+ 412	2
+ 417	10
+ 84	8
+ 978	0
+ 229	0
+ 280	6
+ 798	5
+ 834	4
+ 540	4
+ 504	0
+ 852	6
+ 138	6
+ 512	5
+ 925	1
+ 682	5
+ 567	1
+ 696	10
+ 82	8
+ 830	1
+ 780	1
+ 96	1
+ 697	9
+ 565	5
+ 302	1
+ 900	8
+ 116	8
+ 401	3
+ 307	9
+ 774	2
+ 52	5
+ 690	6
+ 550	4
+ 603	6
+ 166	4
+ 691	9
+ 493	8
+ 7	2
+ 681	6
+ 720	10
+ 677	6
+ 789	8
+ 374	2
+ 46	7
+ 103	8
+ 913	2
+ 276	6
+ 774	8
+ 989	4
+ 457	2
+ 811	1
+ 102	3
+ 935	1
+ 493	6
+ 680	2
+ 601	4
+ 835	4
+ 149	2
+ 580	2
+ 889	7
+ 14	8
+ 838	3
+ 404	6
+ 115	4
+ 989	6
+ 548	8
+ 720	6
+ 103	7
+ 758	6
+ 272	4
+ 810	9
+ 795	6
+ 262	9
+ 852	8
+ 138	7
+ 525	2
+ 543	4
+ 442	9
+ 975	6
+ 340	10
+ 129	9
+ 764	8
+ 537	9
+ 504	3
+ 463	8
+ 733	3
+ 649	5
+ 916	9
+ 471	8
+ 754	6
+ 510	3
+ 761	1
+ 642	2
+ 1000	6
+ 761	3
+ 581	9
+ 227	3
+ 740	8
+ 211	2
+ 58	7
+ 21	8
+ 946	7
+ 318	9
+ 582	8
+ 631	3
+ 398	1
+ 615	2
+ 194	3
+ 363	2
+ 875	1
+ 533	5
+ 16	8
+ 801	8
+ 522	0
+ 0	6
+ 686	0
+ 371	6
+ 692	7
+ 494	3
+ 478	1
+ 610	9
+ 266	2
+ 36	5
+ 483	4
+ 653	4
+ 524	2
+ 814	5
+ 945	6
+ 296	5
+ 627	3
+ 47	3
+ 318	4
+ 944	0
+ 108	4
+ 283	6
+ 564	9
+ 463	8
+ 118	5
+ 290	6
+ 898	9
+ 959	4
+ 129	8
+ 963	1
+ 388	3
+ 541	0
+ 555	6
+ 327	9
+ 6	3
+ 882	1
+ 711	2
+ 700	3
+ 58	2
+ 105	2
+ 662	4
+ 777	6
+ 338	7
+ 983	5
+ 509	9
+ 540	9
+ 205	1
+ 912	8
+ 669	2
+ 633	7
+ 511	5
+ 790	2
+ 680	5
+ 496	7
+ 653	6
+ 915	3
+ 995	7
+ 875	3
+ 429	9
+ 800	9
+ 804	3
+ 835	0
+ 422	7
+ 768	1
+ 987	4
+ 767	5
+ 915	6
+ 720	6
+ 47	2
+ 334	7
+ 817	2
+ 15	8
+ 941	9
+ 145	4
+ 747	9
+ 307	6
+ 286	1
+ 559	7
+ 890	3
+ 798	9
+ 727	6
+ 375	6
+ 122	1
+ 238	2
+ 311	6
+ 869	1
+ 820	9
+ 941	8
+ 773	1
+ 130	5
+ 31	4
+ 70	3
+ 580	6
+ 24	5
+ 956	8
+ 347	7
+ 387	7
+ 325	5
+ 817	6
+ 678	1
+ 134	5
+ 257	10
+ 431	2
+ 715	2
+ 284	8
+ 724	3
+ 281	8
+ 632	9
+ 423	7
+ 331	4
+ 477	7
+ 62	9
+ 400	4
+ 374	2
+ 950	1
+ 346	1
+ 599	6
+ 38	0
+ 800	8
+ 234	1
+ 597	10
+ 399	9
+ 751	0
+ 740	2
+ 686	1
+ 554	2
+ 749	6
+ 28	1
+ 2	4
+ 366	10
+ 454	7
+ 36	1
+ 314	1
+ 83	1
+ 827	3
+ 198	4
+ 275	6
+ 304	0
+ 628	0
+ 201	3
+ 114	8
+ 477	9
+ 370	5
+ 12	4
+ 907	4
+ 324	4
+ 89	4
+ 414	4
+ 435	5
+ 517	3
+ 815	7
+ 687	1
+ 313	10
+ 116	9
+ 34	3
+ 255	1
+ 71	7
+ 12	4
+ 237	0
+ 812	1
+ 401	1
+ 505	5
+ 496	9
+ 893	9
+ 417	4
+ 193	2
+ 125	9
+ 321	4
+ 871	4
+ 380	9
+ 753	6
+ 53	8
+ 366	1
+ 264	6
+ 88	1
+ 747	5
+ 213	3
+ 979	7
+ 171	9
+ 640	6
+ 281	8
+ 819	4
+ 714	1
+ 845	6
+ 577	2
+ 489	3
+ 859	5
+ 154	2
+ 607	4
+ 828	7
+ 495	6
+ 184	7
+ 827	2
+ 417	10
+ 34	1
+ 586	3
+ 890	4
+ 721	6
+ 545	6
+ 188	1
+ 791	7
+ 452	7
+ 219	6
+ 875	8
+ 25	7
+ 521	5
+ 279	7
+ 228	1
+ 868	6
+ 105	9
+ 701	7
+ 217	6
+ 96	9
+ 196	6
+ 505	4
+ 763	3
+ 61	2
+ 946	3
+ 823	8
+ 107	8
+ 525	6
+ 368	8
+ 333	6
+ 910	2
+ 240	0
+ 103	9
+ 706	3
+ 533	8
+ 258	7
+ 443	8
+ 112	2
+ 58	2
+ 423	0
+ 455	2
+ 825	6
+ 93	4
+ 190	5
+ 154	5
+ 56	1
+ 725	3
+ 79	8
+ 237	8
+ 147	8
+ 587	4
+ 498	0
+ 167	6
+ 236	2
+ 785	7
+ 230	2
+ 904	1
+ 801	10
+ 405	10
+ 458	6
+ 515	5
+ 623	2
+ 810	7
+ 67	0
+ 486	2
+ 817	1
+ 619	3
+ 102	8
+ 926	3
+ 11	7
+ 998	2
+ 950	9
+ 297	8
+ 899	7
+ 743	4
+ 261	3
+ 871	9
+ 498	7
+ 585	6
+ 728	1
+ 779	5
+ 144	4
+ 861	2
+ 183	8
+ 585	2
+ 498	6
+ 436	4
+ 485	7
+ 200	4
+ 434	9
+ 741	7
+ 202	6
+ 578	7
+ 293	2
+ 264	0
+ 234	0
+ 566	4
+ 440	4
+ 624	6
+ 213	2
+ 817	7
+ 791	3
+ 160	3
+ 984	4
+ 660	4
+ 303	4
+ 113	5
+ 13	7
+ 204	3
+ 855	5
+ 326	1
+ 511	9
+ 467	10
+ 318	1
+ 573	5
+ 300	4
+ 242	1
+ 642	4
+ 367	6
+ 762	0
+ 45	1
+ 428	2
+ 570	4
+ 851	8
+ 746	7
+ 243	1
+ 795	8
+ 963	3
+ 705	3
+ 354	3
+ 811	7
+ 668	1
+ 744	3
+ 456	1
+ 937	2
+ 137	10
+ 283	6
+ 140	9
+ 5	10
+ 628	8
+ 697	9
+ 823	5
+ 626	8
+ 755	3
+ 66	1
+ 609	9
+ 762	3
+ 931	5
+ 586	4
+ 616	5
+ 604	8
+ 505	9
+ 318	6
+ 741	3
+ 636	4
+ 74	3
+ 241	9
+ 825	9
+ 683	6
+ 197	7
+ 688	8
+ 626	5
+ 82	6
+ 956	7
+ 944	6
+ 192	5
+ 325	7
+ 436	6
+ 342	2
+ 965	10
+ 547	0
+ 312	8
+ 936	1
+ 654	6
+ 717	9
+ 368	4
+ 658	10
+ 855	7
+ 551	8
+ 409	5
+ 382	6
+ 44	7
+ 298	5
+ 349	6
+ 658	3
+ 619	2
+ 354	9
+ 992	3
+ 67	6
+ 909	8
+ 498	3
+ 188	2
+ 271	0
+ 895	8
+ 854	3
+ 318	2
+ 905	4
+ 943	2
+ 843	3
+ 843	5
+ 607	5
+ 705	10
+ 392	7
+ 251	5
+ 343	2
+ 242	8
+ 437	4
+ 995	7
+ 474	9
+ 530	3
+ 195	8
+ 565	1
+ 210	5
+ 303	1
+ 800	1
+ 553	4
+ 609	3
+ 368	0
+ 955	6
+ 460	3
+ 780	7
+ 138	2
+ 133	1
+ 926	6
+ 24	5
+ 935	2
+ 304	5
+ 318	5
+ 8	6
+ 568	8
+ 768	1
+ 216	4
+ 379	6
+ 377	3
+ 204	8
+ 631	10
+ 539	8
+ 202	7
+ 901	1
+ 279	9
+ 584	2
+ 143	9
+ 714	5
+ 403	7
+ 83	10
+ 530	9
+ 92	7
+ 228	5
+ 331	6
+ 804	5
+ 442	4
+ 520	10
+ 203	7
+ 652	1
+ 850	9
+ 29	4
+ 145	2
+ 323	9
+ 633	7
+ 581	7
+ 696	1
+ 567	8
+ 857	8
+ 259	2
+ 400	1
+ 723	8
+ 498	2
+ 822	7
+ 965	5
+ 804	8
+ 405	8
+ 249	6
+ 5	6
+ 409	6
+ 297	10
+ 354	10
+ 100	9
+ 782	10
+ 716	0
+ 146	1
+ 104	9
+ 958	6
+ 112	8
+ 302	1
+ 255	1
+ 892	7
+ 939	1
+ 211	9
+ 713	6
+ 582	0
+ 609	9
+ 4	7
+ 857	8
+ 667	6
+ 828	8
+ 690	9
+ 683	6
+ 533	8
+ 428	8
+ 873	7
+ 942	8
+ 344	9
+ 907	6
+ 825	6
+ 174	4
+ 630	8
+ 343	6
+ 492	2
+ 421	2
+ 774	2
+ 972	5
+ 180	7
+ 112	7
+ 450	5
+ 549	3
+ 224	5
+ 88	6
+ 372	10
+ 122	2
+ 614	3
+ 604	2
+ 77	9
+ 880	6
+ 148	3
+ 728	9
+ 550	7
+ 386	7
+ 354	5
+ 444	8
+ 38	10
+ 127	3
+ 484	2
+ 829	9
+ 209	10
+ 53	8
+ 245	7
+ 68	3
+ 605	9
+ 892	8
+ 249	6
+ 674	8
+ 319	1
+ 529	7
+ 558	10
+ 478	6
+ 967	6
+ 858	5
+ 820	7
+ 307	0
+ 637	4
+ 852	9
+ 19	9
+ 205	6
+ 869	1
+ 376	1
+ 717	1
+ 917	0
+ 111	4
+ 709	7
+ 420	2
+ 265	4
+ 792	1
+ 838	6
+ 809	1
+ 640	4
+ 506	5
+ 328	5
+ 414	5
+ 148	3
+ 630	5
+ 400	3
+ 576	3
+ 382	7
+ 764	1
+ 356	2
+ 279	6
+ 571	1
+ 743	4
+ 683	6
+ 554	3
+ 998	1
+ 816	3
+ 585	2
+ 858	7
+ 512	5
+ 258	9
+ 835	8
+ 230	2
+ 520	10
+ 308	9
+ 177	6
+ 497	7
+ 659	2
+ 157	3
+ 793	7
+ 665	8
+ 772	5
+ 116	4
+ 711	10
+ 90	2
+ 463	3
+ 136	3
+ 181	4
+ 514	7
+ 359	8
+ 577	5
+ 410	1
+ 285	1
+ 314	4
+ 411	1
+ 153	1
+ 897	9
+ 557	0
+ 281	3
+ 988	4
+ 492	5
+ 719	6
+ 748	9
+ 993	3
+ 601	4
+ 85	2
+ 889	5
+ 251	2
+ 564	6
+ 616	10
+ 672	8
+ 50	6
+ 694	6
+ 582	10
+ 875	6
+ 346	4
+ 21	1
+ 994	8
+ 964	10
+ 31	6
+ 340	1
+ 742	2
+ 610	10
+ 402	2
+ 559	0
+ 148	2
+ 786	2
+ 800	5
+ 805	4
+ 455	7
+ 952	8
+ 48	10
+ 866	0
+ 741	8
+ 29	8
+ 395	4
+ 887	1
+ 597	5
+ 132	10
+ 670	7
+ 17	8
+ 921	8
+ 17	7
+ 283	8
+ 103	7
+ 503	1
+ 541	6
+ 27	4
+ 592	8
+ 238	6
+ 539	6
+ 990	4
+ 771	6
+ 922	9
+ 586	6
+ 593	6
+ 411	5
+ 406	4
+ 235	7
+ 250	3
+ 428	8
+ 393	10
+ 303	4
+ 376	9
+ 188	6
+ 516	7
+ 246	5
+ 153	0
+ 93	1
+ 919	7
+ 667	5
+ 282	1
+ 27	7
+ 506	3
+ 378	8
+ 600	8
+ 509	10
+ 775	8
+ 414	2
+ 707	6
+ 765	2
+ 328	0
+ 729	5
+ 28	8
+ 556	9
+ 501	2
+ 460	8
+ 301	5
+ 471	8
+ 749	8
+ 563	3
+ 655	1
+ 342	4
+ 883	8
+ 582	6
+ 357	3
+ 813	7
+ 357	5
+ 166	4
+ 364	7
+ 333	9
+ 945	8
+ 649	2
+ 280	1
+ 53	0
+ 969	6
+ 377	6
+ 688	7
+ 55	6
+ 476	6
+ 161	8
+ 983	10
+ 519	3
+ 516	7
+ 726	9
+ 407	1
+ 745	4
+ 853	4
+ 598	1
+ 514	7
+ 161	5
+ 268	5
+ 107	10
+ 258	2
+ 527	7
+ 799	7
+ 567	8
+ 663	1
+ 123	2
+ 772	8
+ 59	2
+ 909	8
+ 532	8
+ 197	1
+ 894	7
+ 781	1
+ 193	0
+ 593	3
+ 5	9
+ 463	5
+ 585	3
+ 221	2
+ 45	9
+ 238	2
+ 63	0
+ 18	1
+ 189	9
+ 925	7
+ 688	1
+ 851	6
+ 833	6
+ 636	0
+ 681	2
+ 327	7
+ 80	8
+ 217	7
+ 53	4
+ 817	1
+ 322	1
+ 266	4
+ 66	3
+ 506	3
+ 210	4
+ 976	9
+ 554	8
+ 480	4
+ 458	1
+ 414	1
+ 345	7
+ 824	4
+ 532	0
+ 90	6
+ 480	9
+ 683	8
+ 963	9
+ 187	0
+ 234	7
+ 284	4
+ 124	3
+ 342	7
+ 87	8
+ 66	5
+ 938	5
+ 683	3
+ 221	5
+ 708	8
+ 548	8
+ 338	0
+ 706	0
+ 830	7
+ 971	0
+ 699	2
+ 709	10
+ 649	8
+ 244	10
+ 511	3
+ 813	6
+ 875	8
+ 57	6
+ 34	3
+ 66	7
+ 30	6
+ 541	4
+ 642	2
+ 390	5
+ 917	4
+ 487	6
+ 565	2
+ 600	2
+ 29	8
+ 205	5
+ 174	0
+ 118	0
+ 769	2
+ 608	8
+ 452	7
+ 545	5
+ 288	1
+ 851	9
+ 333	2
+ 401	3
+ 601	9
+ 867	2
+ 84	5
+ 380	1
+ 311	6
+ 654	5
+ 604	8
+ 535	4
+ 947	1
+ 176	4
+ 817	7
+ 881	1
+ 808	7
+ 34	1
+ 972	4
+ 392	6
+ 322	3
+ 740	4
+ 725	1
+ 520	0
+ 706	2
+ 521	3
+ 947	1
+ 683	9
+ 199	9
+ 292	0
+ 581	2
+ 120	4
+ 905	2
+ 529	9
+ 588	9
+ 450	9
+ 179	2
+ 318	9
+ 310	8
+ 941	0
+ 13	5
+ 325	10
+ 518	0
+ 853	7
+ 867	1
+ 732	4
+ 318	9
+ 836	2
+ 6	4
+ 100	6
+ 287	6
+ 505	5
+ 741	8
+ 370	1
+ 661	3
+ 67	7
+ 773	4
+ 633	3
+ 401	5
+ 7	3
+ 631	7
+ 716	9
+ 592	6
+ 173	6
+ 917	3
+ 192	2
+ 824	7
+ 670	6
+ 520	0
+ 616	2
+ 351	7
+ 854	1
+ 75	5
+ 414	5
+ 973	4
+ 744	6
+ 160	5
+ 554	8
+ 11	7
+ 349	9
+ 0	5
+ 132	8
+ 239	8
+ 389	8
+ 842	0
+ 941	2
+ 688	8
+ 317	8
+ 282	7
+ 239	3
+ 151	10
+ 860	3
+ 441	4
+ 62	5
+ 141	4
+ 381	1
+ 952	5
+ 965	2
+ 315	4
+ 950	2
+ 359	9
+ 351	0
+ 686	7
+ 809	10
+ 397	0
+ 224	5
+ 30	1
+ 859	5
+ 497	9
+ 924	6
+ 331	3
+ 779	3
+ 818	7
+ 474	1
+ 99	4
+ 291	5
+ 316	6
+ 504	0
+ 308	3
+ 970	7
+ 361	2
+ 254	4
+ 277	1
+ 863	8
+ 33	8
+ 413	4
+ 93	2
+ 648	9
+ 937	1
+ 44	0
+ 546	3
+ 493	9
+ 976	10
+ 863	3
+ 310	8
+ 991	7
+ 27	2
+ 63	3
+ 358	9
+ 78	4
+ 714	5
+ 755	8
+ 682	4
+ 717	6
+ 525	8
+ 654	1
+ 97	1
+ 933	1
+ 144	8
+ 358	5
+ 629	3
+ 126	7
+ 593	2
+ 959	10
+ 115	0
+ 342	8
+ 527	1
+ 635	2
+ 501	4
+ 828	0
+ 114	5
+ 96	2
+ 630	0
+ 284	8
+ 825	6
+ 228	5
+ 990	4
+ 109	6
+ 542	1
+ 534	7
+ 105	9
+ 485	6
+ 974	1
+ 842	5
+ 473	7
+ 500	6
+ 152	6
+ 798	8
+ 625	1
+ 555	4
+ 723	8
+ 903	7
+ 136	0
+ 296	7
+ 80	8
+ 334	2
+ 706	8
+ 818	7
+ 940	7
+ 154	4
+ 329	7
+ 1000	5
+ 249	8
+ 264	9
+ 879	8
+ 323	6
+ 602	2
+ 314	7
+ 239	6
+ 416	3
+ 439	7
+ 505	1
+ 569	3
+ 825	5
+ 982	10
+ 921	3
+ 633	9
+ 793	9
+ 718	1
+ 756	6
+ 877	1
+ 198	5
+ 306	5
+ 217	5
+ 121	6
+ 864	6
+ 382	4
+ 706	10
+ 691	5
+ 460	7
+ 511	4
+ 985	1
+ 302	8
+ 26	0
+ 835	8
+ 617	7
+ 862	8
+ 191	2
+ 326	4
+ 713	4
+ 41	6
+ 8	4
+ 946	7
+ 375	6
+ 245	8
+ 310	8
+ 216	3
+ 900	5
+ 73	9
+ 538	9
+ 708	2
+ 620	6
+ 970	8
+ 738	3
+ 219	5
+ 743	3
+ 28	8
+ 683	10
+ 465	1
+ 611	7
+ 893	9
+ 466	1
+ 215	4
+ 626	3
+ 291	2
+ 196	10
+ 319	8
+ 569	3
+ 627	3
+ 585	8
+ 758	3
+ 107	8
+ 80	8
+ 759	5
+ 848	4
+ 255	7
+ 291	7
+ 849	5
+ 87	5
+ 794	4
+ 640	10
+ 378	10
+ 807	9
+ 249	4
+ 253	8
+ 281	0
+ 162	4
+ 797	2
+ 177	6
+ 787	0
+ 926	0
+ 766	2
+ 763	6
+ 723	9
+ 92	5
+ 228	7
+ 507	6
+ 692	3
+ 552	9
+ 748	8
+ 775	0
+ 817	9
+ 417	6
+ 179	6
+ 170	10
+ 619	1
+ 7	4
+ 312	8
+ 1	0
+ 621	1
+ 552	8
+ 825	1
+ 455	5
+ 373	0
+ 457	1
+ 813	2
+ 151	6
+ 169	6
+ 243	3
+ 161	4
+ 314	8
+ 508	3
+ 166	8
+ 92	2
+ 856	7
+ 259	4
+ 561	1
+ 467	0
+ 600	8
+ 24	1
+ 961	8
+ 289	1
+ 467	5
+ 679	7
+ 806	8
+ 124	1
+ 621	6
+ 441	8
+ 453	5
+ 954	3
+ 245	2
+ 716	8
+ 297	2
+ 823	9
+ 22	8
+ 955	10
+ 684	2
+ 95	2
+ 702	8
+ 862	5
+ 615	10
+ 628	2
+ 617	1
+ 23	1
+ 602	10
+ 378	8
+ 189	1
+ 654	5
+ 276	5
+ 383	3
+ 323	3
+ 281	0
+ 582	4
+ 159	3
+ 151	0
+ 793	8
+ 6	4
+ 2	6
+ 491	0
+ 693	1
+ 2	1
+ 941	2
+ 164	6
+ 677	4
+ 71	1
+ 737	4
+ 398	0
+ 402	10
+ 395	6
+ 264	5
+ 581	1
+ 312	6
+ 477	3
+ 209	10
+ 340	9
+ 61	3
+ 972	0
+ 532	1
+ 596	2
+ 576	7
+ 269	3
+ 61	7
+ 331	5
+ 647	7
+ 23	9
+ 272	6
+ 967	6
+ 190	4
+ 899	4
+ 413	2
+ 300	5
+ 581	3
+ 475	1
+ 408	1
+ 323	10
+ 738	6
+ 297	8
+ 259	6
+ 262	9
+ 354	3
+ 817	6
+ 890	8
+ 211	1
+ 230	1
+ 479	6
+ 350	8
+ 116	9
+ 52	6
+ 44	5
+ 662	4
+ 443	4
+ 959	7
+ 200	2
+ 368	5
+ 124	7
+ 748	9
+ 349	6
+ 727	6
+ 718	10
+ 671	2
+ 599	0
+ 977	7
+ 952	0
+ 307	10
+ 488	10
+ 363	9
+ 369	3
+ 672	6
+ 540	0
+ 31	7
+ 763	8
+ 606	1
+ 417	3
+ 672	1
+ 289	3
+ 333	9
+ 364	3
+ 604	3
+ 339	9
+ 311	8
+ 879	7
+ 759	2
+ 996	4
+ 817	5
+ 471	8
+ 199	2
+ 627	8
+ 346	0
+ 138	0
+ 180	4
+ 362	5
+ 316	7
+ 823	9
+ 41	2
+ 830	4
+ 989	7
+ 26	7
+ 957	0
+ 179	8
+ 557	7
+ 622	8
+ 885	2
+ 562	2
+ 294	7
+ 250	5
+ 128	6
+ 987	4
+ 337	8
+ 363	4
+ 972	2
+ 730	10
+ 901	8
+ 710	9
+ 777	9
+ 632	3
+ 540	3
+ 91	4
+ 503	7
+ 656	8
+ 353	9
+ 271	5
+ 517	3
+ 924	9
+ 68	3
+ 232	0
+ 480	10
+ 2	4
+ 717	7
+ 240	5
+ 600	9
+ 829	1
+ 126	9
+ 564	6
+ 573	2
+ 426	9
+ 126	7
+ 406	6
+ 955	3
+ 498	0
+ 618	7
+ 62	1
+ 692	1
+ 481	4
+ 775	7
+ 904	4
+ 592	7
+ 515	7
+ 652	1
+ 347	2
+ 300	8
+ 150	4
+ 471	6
+ 69	4
+ 887	6
+ 448	5
+ 297	5
+ 605	10
+ 574	1
+ 398	3
+ 806	3
+ 725	4
+ 34	2
+ 116	7
+ 320	5
+ 911	6
+ 237	1
+ 46	7
+ 619	1
+ 133	5
+ 682	6
+ 12	10
+ 91	6
+ 967	7
+ 702	4
+ 14	5
+ 667	7
+ 905	7
+ 978	0
+ 387	3
+ 484	3
+ 918	7
+ 361	10
+ 429	10
+ 78	6
+ 485	8
+ 143	5
+ 738	2
+ 113	7
+ 899	8
+ 70	9
+ 322	7
+ 651	2
+ 438	6
+ 247	8
+ 927	7
+ 124	8
+ 453	5
+ 808	9
+ 464	9
+ 445	9
+ 646	6
+ 446	4
+ 822	6
+ 89	7
+ 374	2
+ 633	7
+ 897	3
+ 923	3
+ 913	2
+ 160	8
+ 902	3
+ 684	4
+ 768	5
+ 237	2
+ 378	7
+ 181	0
+ 270	6
+ 408	1
+ 187	5
+ 814	6
+ 657	4
+ 257	6
+ 731	2
+ 889	6
+ 350	0
+ 484	3
+ 333	2
+ 607	1
+ 661	8
+ 333	0
+ 527	5
+ 63	8
+ 142	5
+ 890	3
+ 968	7
+ 889	6
+ 151	1
+ 179	9
+ 325	1
+ 526	7
+ 116	0
+ 927	4
+ 178	5
+ 550	8
+ 379	9
+ 877	9
+ 398	9
+ 703	5
+ 410	6
+ 868	4
+ 297	8
+ 3	4
+ 903	2
+ 329	2
+ 250	9
+ 903	4
+ 865	8
+ 815	0
+ 366	4
+ 881	7
+ 248	8
+ 651	6
+ 698	4
+ 185	1
+ 947	1
+ 487	2
+ 810	5
+ 691	7
+ 672	0
+ 940	9
+ 875	8
+ 287	7
diff --git a/contrib/bloom/expected/bloom.out b/contrib/bloom/expected/bloom.out
new file mode 100644
index ...f7334fa
*** a/contrib/bloom/expected/bloom.out
--- b/contrib/bloom/expected/bloom.out
***************
*** 0 ****
--- 1,120 ----
+ CREATE EXTENSION bloom;
+ CREATE TABLE tst (
+ 	i	int4,
+ 	t	text
+ );
+ \copy tst from 'data/data'
+ CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+ SET enable_seqscan=on;
+ SET enable_bitmapscan=off;
+ SET enable_indexscan=off;
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16;
+                 QUERY PLAN                 
+ -------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: (i = 16)
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: (i = 16)
+ (5 rows)
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE t = '5';
+                 QUERY PLAN                 
+ -------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: (t = '5'::text)
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: (t = '5'::text)
+ (5 rows)
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+                         QUERY PLAN                        
+ ----------------------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: ((i = 16) AND (t = '5'::text))
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: ((i = 16) AND (t = '5'::text))
+ (5 rows)
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ VACUUM ANALYZE tst;
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ VACUUM FULL tst;
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ RESET enable_seqscan;
+ RESET enable_bitmapscan;
+ RESET enable_indexscan;
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
new file mode 100644
index ...e5c7a86
*** a/contrib/bloom/sql/bloom.sql
--- b/contrib/bloom/sql/bloom.sql
***************
*** 0 ****
--- 1,46 ----
+ CREATE EXTENSION bloom;
+ 
+ CREATE TABLE tst (
+ 	i	int4,
+ 	t	text
+ );
+ 
+ \copy tst from 'data/data'
+ 
+ CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+ 
+ SET enable_seqscan=on;
+ SET enable_bitmapscan=off;
+ SET enable_indexscan=off;
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16;
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE t = '5';
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ VACUUM ANALYZE tst;
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ VACUUM FULL tst;
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ RESET enable_seqscan;
+ RESET enable_bitmapscan;
+ RESET enable_indexscan;
diff --git a/contrib/bloom/t/001_wal.pl b/contrib/bloom/t/001_wal.pl
new file mode 100644
index ...c0f8e64
*** a/contrib/bloom/t/001_wal.pl
--- b/contrib/bloom/t/001_wal.pl
***************
*** 0 ****
--- 1,75 ----
+ # Test generic xlog record work for bloom index replication.
+ use strict;
+ use warnings;
+ use PostgresNode;
+ use TestLib;
+ use Test::More tests => 31;
+ 
+ my $node_master;
+ my $node_standby;
+ 
+ # Run few queries on both master and stanbdy and check their results match.
+ sub test_index_replay
+ {
+ 	my ($test_name) = @_;
+ 
+ 	# Wait for standby to catch up
+ 	my $applname = $node_standby->name;
+ 	my $caughtup_query =
+ 		"SELECT pg_current_xlog_location() <= write_location FROM pg_stat_replication WHERE application_name = '$applname';";
+ 	$node_master->poll_query_until('postgres', $caughtup_query)
+ 	  or die "Timed out while waiting for standby 1 to catch up";
+ 
+ 	my $queries = qq(SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ SELECT * FROM tst WHERE i = 0;
+ SELECT * FROM tst WHERE i = 3;
+ SELECT * FROM tst WHERE t = 'b';
+ SELECT * FROM tst WHERE t = 'f';
+ SELECT * FROM tst WHERE i = 3 AND t = 'c';
+ SELECT * FROM tst WHERE i = 7 AND t = 'e';
+ );
+ 
+ 	# Run test queries and compare their result
+ 	my $master_result = $node_master->psql("postgres", $queries);
+ 	my $standby_result = $node_standby->psql("postgres", $queries);
+ 
+ 	is($master_result, $standby_result, "$test_name: query result matches");
+ }
+ 
+ # Initialize master node
+ $node_master = get_new_node('master');
+ $node_master->init(allows_streaming => 1);
+ $node_master->start;
+ my $backup_name = 'my_backup';
+ 
+ # Take backup
+ $node_master->backup($backup_name);
+ 
+ # Create streaming standby linking to master
+ $node_standby = get_new_node('standby');
+ $node_standby->init_from_backup($node_master, $backup_name,
+ 	has_streaming => 1);
+ $node_standby->start;
+ 
+ # Create some bloom index on master
+ $node_master->psql("postgres", "CREATE EXTENSION bloom;");
+ $node_master->psql("postgres", "CREATE TABLE tst (i int4, t text);");
+ $node_master->psql("postgres", "INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,100000) i;");
+ $node_master->psql("postgres", "CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);");
+ 
+ # Test that queries give same result
+ test_index_replay('initial');
+ 
+ # Run 10 cycles of table modification. Run test queries after each modification.
+ for (my $i = 1; $i <= 10; $i++)
+ {
+ 	$node_master->psql("postgres", "DELETE FROM tst WHERE i = $i;");
+ 	test_index_replay("delete $i");
+ 	$node_master->psql("postgres", "VACUUM tst;");
+ 	test_index_replay("vacuum $i");
+ 	my ($start, $end) = (100001 + ($i - 1) * 10000, 100000 + $i * 10000);
+ 	$node_master->psql("postgres", "INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series($start,$end) i;");
+ 	test_index_replay("insert $i");
+ }
diff --git a/doc/src/sgml/bloom.sgml b/doc/src/sgml/bloom.sgml
new file mode 100644
index ...c207e6d
*** a/doc/src/sgml/bloom.sgml
--- b/doc/src/sgml/bloom.sgml
***************
*** 0 ****
--- 1,218 ----
+ <!-- doc/src/sgml/bloom.sgml -->
+ 
+ <sect1 id="bloom" xreflabel="bloom">
+  <title>bloom</title>
+ 
+  <indexterm zone="bloom">
+   <primary>bloom</primary>
+  </indexterm>
+ 
+  <para>
+   <literal>bloom</> is a contrib which implements index access method.  It comes
+   as example of custom access methods and generic WAL records usage.  But it
+   is also useful itself.
+  </para>
+ 
+  <sect2>
+   <title>Introduction</title>
+ 
+   <para>
+    Implementation of
+    <ulink url="http://en.wikipedia.org/wiki/Bloom_filter">Bloom filter</ulink>
+    allows fast exclusion of non-candidate tuples.
+    Since signature is a lossy representation of all indexed attributes, 
+    search results should be rechecked using heap information. 
+    User can specify signature length (in uint16, default is 5) and the number of 
+    bits, which can be setted, per attribute (1 < colN < 2048).
+   </para>
+ 
+   <para>
+    This index is useful if table has many attributes and queries can include
+    their arbitary combinations.  Traditional <literal>btree</> index is faster
+    than bloom index, but it'd require too many indexes to support all possible 
+    queries, while one need only one bloom index.  Bloom index supports only 
+    equality comparison.  Since it's a signature file, not a tree, it always
+    should be readed fully, but sequentially, so index search performance is 
+    constant and doesn't depend on a query. 
+   </para>
+  </sect2>
+ 
+  <sect2>
+   <title>Parameters</title>
+ 
+   <para>
+    <literal>bloom</> indexes accept following parameters in <literal>WITH</>
+    clause.
+   </para>
+ 
+    <variablelist>
+    <varlistentry>
+     <term><literal>length</></term>
+     <listitem>
+      <para>
+       Length of signature in uint16 type values
+      </para>
+     </listitem>
+    </varlistentry>
+    </variablelist>
+    <variablelist>
+    <varlistentry>
+     <term><literal>col1 &mdash; col16</></term>
+     <listitem>
+      <para>
+       Number of bits for corresponding column
+      </para>
+     </listitem>
+    </varlistentry>
+    </variablelist>
+  </sect2>
+ 
+  <sect2>
+   <title>Examples</title>
+ 
+   <para>
+    Example of index definition is given below.
+   </para>
+ 
+ <programlisting>
+ CREATE INDEX bloomidx ON tbloom(i1,i2,i3) 
+        WITH (length=5, col1=2, col2=2, col3=4);
+ </programlisting>
+ 
+   <para>
+    Here, we create bloom index with signature length 80 bits and attributes
+    i1, i2  mapped to 2 bits, attribute i3 - to 4 bits.
+   </para>
+ 
+   <para>
+    Example of index definition and usage is given below.
+   </para>
+ 
+ <programlisting>
+ CREATE TABLE tbloom AS
+ SELECT
+     random()::int as i1,
+     random()::int as i2,
+     random()::int as i3,
+     random()::int as i4,
+     random()::int as i5,
+     random()::int as i6,
+     random()::int as i7,
+     random()::int as i8,
+     random()::int as i9,
+     random()::int as i10,
+     random()::int as i11,
+     random()::int as i12,
+     random()::int as i13
+ FROM
+     generate_series(1,1000);
+ CREATE INDEX bloomidx ON tbloom USING
+              bloom (i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12);
+ SELECT pg_relation_size('bloomidx');
+ CREATE index btree_idx ON tbloom(i1,i2,i3,i4,i5,i6,i7,i8,i9,i10,i11,i12);
+ SELECT pg_relation_size('btree_idx');
+ </programlisting>
+ 
+ <programlisting>
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                                    QUERY PLAN
+ -----------------------------------------------------------------------------------------------------------------
+  Bitmap Heap Scan on tbloom  (cost=1.50..5.52 rows=1 width=52) (actual time=0.057..0.057 rows=0 loops=1)
+    Recheck Cond: ((i2 = 20) AND (i10 = 15))
+    ->  Bitmap Index Scan on bloomidx  (cost=0.00..1.50 rows=1 width=0) (actual time=0.041..0.041 rows=9 loops=1)
+          Index Cond: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.081 ms
+ (5 rows)
+ </programlisting>
+ 
+   <para>
+    Seqscan is slow.
+   </para>
+ 
+ <programlisting>
+ =# SET enable_bitmapscan = off;
+ =# SET enable_indexscan = off;
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                             QUERY PLAN
+ --------------------------------------------------------------------------------------------------
+  Seq Scan on tbloom  (cost=0.00..25.00 rows=1 width=52) (actual time=0.162..0.162 rows=0 loops=1)
+    Filter: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.181 ms
+ (3 rows)
+ </programlisting>
+ 
+  <para>
+   Btree index will be not used for this query.
+  </para>
+ 
+ <programlisting>
+ =# DROP INDEX bloomidx;
+ =# CREATE INDEX btree_idx ON tbloom(i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12);
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                             QUERY PLAN
+ --------------------------------------------------------------------------------------------------
+  Seq Scan on tbloom (cost=0.00..25.00 rows=1 width=52) (actual time=0.210..0.210 rows=0 loops=1)
+    Filter: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.250 ms
+ (3 rows)
+ </programlisting>
+  </sect2>
+ 
+  <sect2>
+   <title>Opclass interface</title>
+ 
+   <para>
+    Bloom opclass interface is simple.  It requires 1 supporting function:
+    hash function for indexing datatype.  And it provides 1 search operator:
+    equality operator.  The example below shows <literal>opclass</> definition
+    for <literal>text</> datatype.
+   </para>
+ 
+ <programlisting>
+ CREATE OPERATOR CLASS text_ops
+ DEFAULT FOR TYPE text USING bloom AS
+     OPERATOR    1   =(text, text),
+     FUNCTION    1   hashtext(text);
+ </programlisting>
+  </sect2>
+ 
+  <sect2>
+   <title>Limitation</title>
+   <para>
+ 
+    <itemizedlist>
+     <listitem>
+      <para>
+       For now, only opclasses for <literal>int4</>, <literal>text</> comes
+       with contrib.  However, users may define more of them.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       Only <literal>=</literal> operator is supported for search now.  But it's
+       possible to add support of arrays with contains and intersection
+       operations in future.
+      </para>
+     </listitem>
+    </itemizedlist>
+   </para>
+  </sect2>
+ 
+  <sect2>
+   <title>Authors</title>
+ 
+   <para>
+    Teodor Sigaev <email>teodor@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+ 
+   <para>
+    Alexander Korotkov <email>a.korotkov@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+ 
+   <para>
+    Oleg Bartunov <email>obartunov@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+  </sect2>
+ 
+ </sect1>
diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
new file mode 100644
index 1b3d2d9..c38b2f9
*** a/doc/src/sgml/contrib.sgml
--- b/doc/src/sgml/contrib.sgml
*************** CREATE EXTENSION <replaceable>module_nam
*** 105,110 ****
--- 105,111 ----
   &adminpack;
   &auth-delay;
   &auto-explain;
+  &bloom;
   &btree-gin;
   &btree-gist;
   &chkpass;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
new file mode 100644
index a12fee7..4a93ec2
*** a/doc/src/sgml/filelist.sgml
--- b/doc/src/sgml/filelist.sgml
***************
*** 106,111 ****
--- 106,112 ----
  <!ENTITY adminpack       SYSTEM "adminpack.sgml">
  <!ENTITY auth-delay      SYSTEM "auth-delay.sgml">
  <!ENTITY auto-explain    SYSTEM "auto-explain.sgml">
+ <!ENTITY bloom           SYSTEM "bloom.sgml">
  <!ENTITY btree-gin       SYSTEM "btree-gin.sgml">
  <!ENTITY btree-gist      SYSTEM "btree-gist.sgml">
  <!ENTITY chkpass         SYSTEM "chkpass.sgml">
#51Michael Paquier
michael.paquier@gmail.com
In reply to: Alexander Korotkov (#50)
Re: WIP: Access method extendability

On Mon, Feb 29, 2016 at 7:42 PM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:

On Fri, Feb 19, 2016 at 4:08 AM, Michael Paquier <michael.paquier@gmail.com>
wrote:

This is basically a copy of RewindTest.pm. This is far from generic.
If this patch gets committed first I would suggest to wait for the
more-generic routines that would be added in PostgresNode.pm and then
come back to it.

Yes, that's it. Now, with committed changes to PostgresNode.pm, I get rid of
separate WALTest.pm.

The test is cleaner in this shape, thanks.

+ # Run few queries on both master and stanbdy and check their results match.
s/stanbdy/standby
-- 
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#52Petr Jelinek
petr@2ndquadrant.com
In reply to: Alexander Korotkov (#50)
Re: WIP: Access method extendability

Hi,

I went over the latest version of this, here are my notes.

create-am.9:

+ 		case DO_ACCESS_METHOD:
+ 			snprintf(buf, bufsize,
+ 					 "");
+ 			return;

Missing the actual description.

+ 	appendPQExpBuffer(q, "CREATE ACCESS METHOD %s HANDLER %s;\n",
+ 					  qamname, aminfo->amhandler);

Generates invalid syntax (missing TYPE).

I don't like that you hardcode 'i' everywhere in the code as am type
index. It would be better to define it in pg_am.h and use that.

I was also debating in my head if amtype is correct name as it maps to
relkind so amkind might be better name for the column, otoh then it
won't map to the syntax we have agreed on for CREATE ACCESS METHOD so I
guess there is no ideal name here.

object_type_map record is missing, as is getObjectTypeDescription and
getObjectIdentityParts support

(you can check what I sent as part of sequence access methods where I
fixed these things, otherwise it reuses most of your code)

generic-xlog.9:
Not much to say here, the api makes sense to me, it will have
performance impact but don't see how we can do this generically without
callbacks to extension in any better way.

One thing I don't understand is why there is support for unlogged
relations. Shouldn't that be handled by whoever is using this to
actually not call this at all? It's just call to RelationNeedsWAL()
nothing to hard and hidden magic like not doing anything with WAL for
the unlogged tables is seldomly good idea.

Another small thing is that we put the API explanation comments into .c
file not .h file.

Didn't look at the bloom index too deeply yet.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#53Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Petr Jelinek (#52)
3 attachment(s)
Re: WIP: Access method extendability

Hi!

Next revision of patches is attached.

On Wed, Mar 9, 2016 at 4:56 AM, Petr Jelinek <petr@2ndquadrant.com> wrote:

I went over the latest version of this, here are my notes.

create-am.9:

+               case DO_ACCESS_METHOD:
+                       snprintf(buf, bufsize,
+                                        "");
+                       return;

Fixed.

Missing the actual description.

+       appendPQExpBuffer(q, "CREATE ACCESS METHOD %s HANDLER %s;\n",
+                                         qamname, aminfo->amhandler);

Generates invalid syntax (missing TYPE).

Fixed.

I don't like that you hardcode 'i' everywhere in the code as am type index.

It would be better to define it in pg_am.h and use that.

Fixed.

I was also debating in my head if amtype is correct name as it maps to

relkind so amkind might be better name for the column, otoh then it won't
map to the syntax we have agreed on for CREATE ACCESS METHOD so I guess
there is no ideal name here.

I leave it amtype for now.

object_type_map record is missing, as is getObjectTypeDescription and
getObjectIdentityParts support

Fixed.

(you can check what I sent as part of sequence access methods where I fixed

these things, otherwise it reuses most of your code)

Thank you! I used it as cheat sheet.

generic-xlog.9:
Not much to say here, the api makes sense to me, it will have performance
impact but don't see how we can do this generically without callbacks to
extension in any better way.

Yep, performance might be much less than with other resource managers. But
that seems to be the only way to do this in extension since we don't want
extensions to have redo callbacks. Also, this is the basic version. There
could be more advances in future. In the previous version I has more
effective handling of data shift inside page. But it was removed for the
sake of simplicity to increase chance to commit.

One thing I don't understand is why there is support for unlogged

relations. Shouldn't that be handled by whoever is using this to actually
not call this at all? It's just call to RelationNeedsWAL() nothing to hard
and hidden magic like not doing anything with WAL for the unlogged tables
is seldomly good idea.

Besides formation of xlog record generic xlog also does copying page images
and atomic replacement of them inside critical section. For sure, it could
be done without generic xlog. But I found it useful for generic xlog to
support this since code of extension becomes much more simple and elegant.
But I can remove it if you insist.

Another small thing is that we put the API explanation comments into .c

file not .h file.

Explanation was moved from .h to .c.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

Attachments:

create-am.10.patchapplication/octet-stream; name=create-am.10.patchDownload
diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml
new file mode 100644
index 5f7befb..e5f8f1c
*** a/doc/src/sgml/indexam.sgml
--- b/doc/src/sgml/indexam.sgml
***************
*** 58,63 ****
--- 58,69 ----
    </para>
  
    <para>
+    Index access access methods can be defined and dropped using
+    <xref linkend="sql-createaccessmethod"> and
+     <xref linkend="sql-dropaccessmethod"> SQL commands respectively.
+   </para>
+ 
+   <para>
     An index access method handler function must be declared to accept a
     single argument of type <type>internal</> and to return the
     pseudo-type <type>index_am_handler</>.  The argument is a dummy value that
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
new file mode 100644
index bf95453..77667bd
*** a/doc/src/sgml/ref/allfiles.sgml
--- b/doc/src/sgml/ref/allfiles.sgml
*************** Complete list of usable sgml source file
*** 52,57 ****
--- 52,58 ----
  <!ENTITY commit             SYSTEM "commit.sgml">
  <!ENTITY commitPrepared     SYSTEM "commit_prepared.sgml">
  <!ENTITY copyTable          SYSTEM "copy.sgml">
+ <!ENTITY createAccessMethod SYSTEM "create_access_method.sgml">
  <!ENTITY createAggregate    SYSTEM "create_aggregate.sgml">
  <!ENTITY createCast         SYSTEM "create_cast.sgml">
  <!ENTITY createCollation    SYSTEM "create_collation.sgml">
*************** Complete list of usable sgml source file
*** 94,99 ****
--- 95,101 ----
  <!ENTITY delete             SYSTEM "delete.sgml">
  <!ENTITY discard            SYSTEM "discard.sgml">
  <!ENTITY do                 SYSTEM "do.sgml">
+ <!ENTITY dropAccessMethod   SYSTEM "drop_access_method.sgml">
  <!ENTITY dropAggregate      SYSTEM "drop_aggregate.sgml">
  <!ENTITY dropCast           SYSTEM "drop_cast.sgml">
  <!ENTITY dropCollation      SYSTEM "drop_collation.sgml">
diff --git a/doc/src/sgml/ref/create_access_method.sgml b/doc/src/sgml/ref/create_access_method.sgml
new file mode 100644
index ...f255ecc
*** a/doc/src/sgml/ref/create_access_method.sgml
--- b/doc/src/sgml/ref/create_access_method.sgml
***************
*** 0 ****
--- 1,120 ----
+ <!--
+ doc/src/sgml/ref/create_access_method.sgml
+ PostgreSQL documentation
+ -->
+ 
+ <refentry id="SQL-CREATEACCESSMETHOD">
+  <indexterm zone="sql-createaccessmethod">
+   <primary>CREATE ACCESS METHOD</primary>
+  </indexterm>
+ 
+  <refmeta>
+   <refentrytitle>CREATE ACCESS METHOD</refentrytitle>
+   <manvolnum>7</manvolnum>
+   <refmiscinfo>SQL - Language Statements</refmiscinfo>
+  </refmeta>
+ 
+  <refnamediv>
+   <refname>CREATE ACCESS METHOD</refname>
+   <refpurpose>define a new access method</refpurpose>
+  </refnamediv>
+ 
+  <refsynopsisdiv>
+ <synopsis>
+ CREATE ACCESS METHOD <replaceable class="parameter">name</replaceable>
+     TYPE INDEX
+     HANDLER <replaceable class="parameter">handler_function</replaceable>
+ </synopsis>
+  </refsynopsisdiv>
+ 
+  <refsect1>
+   <title>Description</title>
+ 
+   <para>
+    <command>CREATE ACCESS METHOD</command> creates a new access method.
+   </para>
+ 
+   <para>
+    The access method name must be unique within the database.
+   </para>
+ 
+   <para>
+    Only superusers can define new access methods.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Parameters</title>
+ 
+   <variablelist>
+    <varlistentry>
+     <term><replaceable class="parameter">name</replaceable></term>
+     <listitem>
+      <para>
+       The name of the access method to be created.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>TYPE INDEX</literal></term>
+     <listitem>
+      <para>
+       This clause specifies type of access method to define.
+       For now, there are only index access methods.  But intentionally
+       there would be other types of access methods.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>HANDLER <replaceable class="parameter">handler_function</replaceable></literal></term>
+     <listitem>
+      <para><replaceable class="parameter">handler_function</replaceable> is the
+       name of a previously registered function that will be called to
+       retrieve the struct which contains required parameters and functions
+       of access method to the core.  The handler function must take single
+       argument of type <type>internal</>, and its return type must be
+       <type>index_am_handler</type>.
+      </para>
+ 
+      <para>
+       See <xref linkend="index-api"> for index access methods API.
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Examples</title>
+ 
+   <para>
+    Create an access method <literal>bloom</> with
+    handler function <literal>blhandler</>:
+ <programlisting>
+ CREATE ACCESS METHOD bloom TYPE INDEX HANDLER blhandler;
+ </programlisting>
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Compatibility</title>
+ 
+   <para>
+    <command>CREATE ACCESS METHOD</command> is a
+    <productname>PostgreSQL</> extension.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>See Also</title>
+ 
+   <simplelist type="inline">
+    <member><xref linkend="sql-dropaccessmethod"></member>
+    <member><xref linkend="sql-createopclass"></member>
+    <member><xref linkend="sql-createopfamily"></member>
+   </simplelist>
+  </refsect1>
+ 
+ </refentry>
diff --git a/doc/src/sgml/ref/drop_access_method.sgml b/doc/src/sgml/ref/drop_access_method.sgml
new file mode 100644
index ...354b923
*** a/doc/src/sgml/ref/drop_access_method.sgml
--- b/doc/src/sgml/ref/drop_access_method.sgml
***************
*** 0 ****
--- 1,112 ----
+ <!--
+ doc/src/sgml/ref/drop_access_method.sgml
+ PostgreSQL documentation
+ -->
+ 
+ <refentry id="SQL-DROPACCESSMETHOD">
+  <indexterm zone="sql-dropaccessmethod">
+   <primary>DROP ACCESS METHOD</primary>
+  </indexterm>
+ 
+  <refmeta>
+   <refentrytitle>DROP ACCESS METHOD</refentrytitle>
+   <manvolnum>7</manvolnum>
+   <refmiscinfo>SQL - Language Statements</refmiscinfo>
+  </refmeta>
+ 
+  <refnamediv>
+   <refname>DROP ACCESS METHOD</refname>
+   <refpurpose>remove an access method</refpurpose>
+  </refnamediv>
+ 
+  <refsynopsisdiv>
+ <synopsis>
+ DROP ACCESS METHOD [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ CASCADE | RESTRICT ]
+ </synopsis>
+  </refsynopsisdiv>
+ 
+  <refsect1>
+   <title>Description</title>
+ 
+   <para>
+    <command>DROP ACCESS METHOD</command> removes an existing access method.
+    Only superusers can drop access methods.
+   </para>
+ 
+   <para>
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Parameters</title>
+ 
+   <variablelist>
+    <varlistentry>
+     <term><literal>IF EXISTS</literal></term>
+     <listitem>
+      <para>
+       Do not throw an error if the access method does not exist.
+       A notice is issued in this case.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><replaceable class="parameter">name</replaceable></term>
+     <listitem>
+      <para>
+       The name of an existing access method.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>CASCADE</literal></term>
+     <listitem>
+      <para>
+       Automatically drop objects that depend on the access method
+       (such as operator classes, operator families, indexes).
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>RESTRICT</literal></term>
+     <listitem>
+      <para>
+       Refuse to drop the access method if any objects depend on it.
+       This is the default.
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Examples</title>
+ 
+   <para>
+    Drop the access method <literal>bloom</>:
+ <programlisting>
+ DROP ACCESS METHOD bloom;
+ </programlisting></para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Compatibility</title>
+ 
+   <para>
+    <command>DROP ACCESS METHOD</command> is a
+    <productname>PostgreSQL</> extension.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>See Also</title>
+ 
+   <simplelist type="inline">
+    <member><xref linkend="sql-createaccessmethod"></member>
+   </simplelist>
+  </refsect1>
+ 
+ </refentry>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
new file mode 100644
index 03020df..8acdff1
*** a/doc/src/sgml/reference.sgml
--- b/doc/src/sgml/reference.sgml
***************
*** 80,85 ****
--- 80,86 ----
     &commit;
     &commitPrepared;
     &copyTable;
+    &createAccessMethod;
     &createAggregate;
     &createCast;
     &createCollation;
***************
*** 122,127 ****
--- 123,129 ----
     &delete;
     &discard;
     &do;
+    &dropAccessMethod;
     &dropAggregate;
     &dropCast;
     &dropCollation;
diff --git a/src/backend/access/index/amapi.c b/src/backend/access/index/amapi.c
new file mode 100644
index bda166a..e9b9b3f
*** a/src/backend/access/index/amapi.c
--- b/src/backend/access/index/amapi.c
*************** GetIndexAmRoutineByAmId(Oid amoid)
*** 62,67 ****
--- 62,74 ----
  			 amoid);
  	amform = (Form_pg_am) GETSTRUCT(tuple);
  
+ 	/* Check if it's index access method */
+ 	if (amform->amtype != AMTYPE_INDEX)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("access method \"%s\" type is not index",
+ 						NameStr(amform->amname))));
+ 
  	amhandler = amform->amhandler;
  
  	/* Complain if handler OID is invalid */
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
new file mode 100644
index c48e37b..a86a488
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 20,25 ****
--- 20,26 ----
  #include "catalog/heap.h"
  #include "catalog/index.h"
  #include "catalog/objectaccess.h"
+ #include "catalog/pg_am.h"
  #include "catalog/pg_amop.h"
  #include "catalog/pg_amproc.h"
  #include "catalog/pg_attrdef.h"
*************** static const Oid object_classes[] = {
*** 160,166 ****
  	ExtensionRelationId,		/* OCLASS_EXTENSION */
  	EventTriggerRelationId,		/* OCLASS_EVENT_TRIGGER */
  	PolicyRelationId,			/* OCLASS_POLICY */
! 	TransformRelationId			/* OCLASS_TRANSFORM */
  };
  
  
--- 161,168 ----
  	ExtensionRelationId,		/* OCLASS_EXTENSION */
  	EventTriggerRelationId,		/* OCLASS_EVENT_TRIGGER */
  	PolicyRelationId,			/* OCLASS_POLICY */
! 	TransformRelationId,		/* OCLASS_TRANSFORM */
! 	AccessMethodRelationId		/* OCLASS_AM */
  };
  
  
*************** doDeletion(const ObjectAddress *object, 
*** 1270,1275 ****
--- 1272,1280 ----
  
  		case OCLASS_TRANSFORM:
  			DropTransformById(object->objectId);
+ 
+ 		case OCLASS_AM:
+ 			RemoveAccessMethodById(object->objectId);
  			break;
  
  		default:
*************** getObjectClass(const ObjectAddress *obje
*** 2415,2420 ****
--- 2420,2428 ----
  
  		case TransformRelationId:
  			return OCLASS_TRANSFORM;
+ 
+ 		case AccessMethodRelationId:
+ 			return OCLASS_AM;
  	}
  
  	/* shouldn't get here */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
new file mode 100644
index d2aaa6d..78eff1e
*** a/src/backend/catalog/objectaddress.c
--- b/src/backend/catalog/objectaddress.c
*************** static const ObjectPropertyType ObjectPr
*** 438,443 ****
--- 438,455 ----
  		Anum_pg_type_typacl,
  		ACL_KIND_TYPE,
  		true
+ 	},
+ 	{
+ 		AccessMethodRelationId,
+ 		AmOidIndexId,
+ 		AMOID,
+ 		AMNAME,
+ 		Anum_pg_am_amname,
+ 		InvalidAttrNumber,
+ 		InvalidAttrNumber,
+ 		InvalidAttrNumber,
+ 		-1,
+ 		true
  	}
  };
  
*************** static const struct object_type_map
*** 640,645 ****
--- 652,661 ----
  	/* OCLASS_TRANSFORM */
  	{
  		"transform", OBJECT_TRANSFORM
+ 	},
+ 	/* OCLASS_AM */
+ 	{
+ 		"access method", OBJECT_ACCESS_METHOD
  	}
  };
  
*************** static ObjectAddress get_object_address_
*** 674,679 ****
--- 690,697 ----
  							   List *objargs, bool missing_ok);
  static ObjectAddress get_object_address_defacl(List *objname, List *objargs,
  						  bool missing_ok);
+ static ObjectAddress get_object_address_am(ObjectType objtype, List *objname,
+ 						bool missing_ok);
  static const ObjectPropertyType *get_object_property_data(Oid class_id);
  
  static void getRelationDescription(StringInfo buffer, Oid relid);
*************** get_object_address(ObjectType objtype, L
*** 913,918 ****
--- 931,939 ----
  				address = get_object_address_defacl(objname, objargs,
  													missing_ok);
  				break;
+ 			case OBJECT_ACCESS_METHOD:
+ 				address = get_object_address_am(objtype, objname, missing_ok);
+ 				break;
  			default:
  				elog(ERROR, "unrecognized objtype: %d", (int) objtype);
  				/* placate compiler, in case it thinks elog might return */
*************** get_object_address_opcf(ObjectType objty
*** 1489,1495 ****
  	ObjectAddress address;
  
  	/* XXX no missing_ok support here */
! 	amoid = get_am_oid(strVal(linitial(objname)), false);
  	objname = list_copy_tail(objname, 1);
  
  	switch (objtype)
--- 1510,1516 ----
  	ObjectAddress address;
  
  	/* XXX no missing_ok support here */
! 	amoid = get_am_oid(strVal(linitial(objname)), AMTYPE_INDEX, false);
  	objname = list_copy_tail(objname, 1);
  
  	switch (objtype)
*************** get_object_address_opcf(ObjectType objty
*** 1516,1521 ****
--- 1537,1601 ----
  }
  
  /*
+  * Find the ObjectAddress for an access method.
+  */
+ static ObjectAddress
+ get_object_address_am(ObjectType objtype, List *objname, bool missing_ok)
+ {
+ 	ObjectAddress	address;
+ 	char		   *amname, *catalogname;
+ 	Type		tup;
+ 
+ 	switch (list_length(objname))
+ 	{
+ 		case 1:
+ 			amname = strVal(linitial(objname));
+ 			break;
+ 		case 2:
+ 			catalogname = strVal(linitial(objname));
+ 			amname = strVal(lsecond(objname));
+ 
+ 			/*
+ 			 * We check the catalog name and then ignore it.
+ 			 */
+ 			if (strcmp(catalogname, get_database_name(MyDatabaseId)) != 0)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				  errmsg("cross-database references are not implemented: %s",
+ 						 NameListToString(objname))));
+ 			break;
+ 		default:
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_SYNTAX_ERROR),
+ 				errmsg("improper access method name (too many dotted names): %s",
+ 					   NameListToString(objname))));
+ 			break;
+ 	}
+ 
+ 	address.classId = AccessMethodRelationId;
+ 	address.objectId = InvalidOid;
+ 	address.objectSubId = 0;
+ 
+ 	tup = SearchSysCache1(AMNAME, PointerGetDatum(amname));
+ 	if (!HeapTupleIsValid(tup))
+ 	{
+ 		/* Access method is missing, report error if needed */
+ 		if (!missing_ok)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 					 errmsg("access method \"%s\" does not exist",
+ 							amname)));
+ 		return address;
+ 	}
+ 
+ 	/* Access method is found, return its oid */
+ 	address.objectId = HeapTupleGetOid(tup);
+ 	ReleaseSysCache(tup);
+ 
+ 	return address;
+ }
+ 
+ /*
   * Find the ObjectAddress for an opclass/opfamily member.
   *
   * (The returned address corresponds to a pg_amop/pg_amproc object).
*************** check_object_ownership(Oid roleid, Objec
*** 2179,2184 ****
--- 2259,2265 ----
  			break;
  		case OBJECT_TSPARSER:
  		case OBJECT_TSTEMPLATE:
+ 		case OBJECT_ACCESS_METHOD:
  			/* We treat these object types as being owned by superusers */
  			if (!superuser_arg(roleid))
  				ereport(ERROR,
*************** getObjectDescription(const ObjectAddress
*** 3129,3134 ****
--- 3210,3230 ----
  				break;
  			}
  
+ 		case OCLASS_AM:
+ 			{
+ 				HeapTuple	tup;
+ 
+ 				tup = SearchSysCache1(AMOID,
+ 									  ObjectIdGetDatum(object->objectId));
+ 				if (!HeapTupleIsValid(tup))
+ 					elog(ERROR, "cache lookup failed for access method %u",
+ 						 object->objectId);
+ 				appendStringInfo(&buffer, _("access method %s"),
+ 							NameStr(((Form_pg_am) GETSTRUCT(tup))->amname));
+ 				ReleaseSysCache(tup);
+ 				break;
+ 			}
+ 
  		default:
  			appendStringInfo(&buffer, "unrecognized object %u %u %d",
  							 object->classId,
*************** getObjectTypeDescription(const ObjectAdd
*** 3610,3615 ****
--- 3706,3715 ----
  			appendStringInfoString(&buffer, "transform");
  			break;
  
+ 		case OCLASS_AM:
+ 			appendStringInfoString(&buffer, "access method");
+ 			break;
+ 
  		default:
  			appendStringInfo(&buffer, "unrecognized %u", object->classId);
  			break;
*************** getObjectIdentityParts(const ObjectAddre
*** 4566,4571 ****
--- 4666,4685 ----
  			}
  			break;
  
+ 		case OCLASS_AM:
+ 			{
+ 				char	   *amname;
+ 
+ 				amname = get_am_name(object->objectId);
+ 				if (!amname)
+ 					elog(ERROR, "cache lookup failed for access method %u",
+ 						 object->objectId);
+ 				appendStringInfoString(&buffer, quote_identifier(amname));
+ 				if (objname)
+ 					*objname = list_make1(amname);
+ 			}
+ 			break;
+ 
  		default:
  			appendStringInfo(&buffer, "unrecognized object %u %u %d",
  							 object->classId,
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
new file mode 100644
index b1ac704..6b3742c
*** a/src/backend/commands/Makefile
--- b/src/backend/commands/Makefile
*************** subdir = src/backend/commands
*** 12,18 ****
  top_builddir = ../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o  \
  	collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
  	dbcommands.o define.o discard.o dropcmds.o \
  	event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
--- 12,18 ----
  top_builddir = ../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
  	collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
  	dbcommands.o define.o discard.o dropcmds.o \
  	event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
new file mode 100644
index ...3d7f5ec
*** a/src/backend/commands/amcmds.c
--- b/src/backend/commands/amcmds.c
***************
*** 0 ****
--- 1,277 ----
+ /*-------------------------------------------------------------------------
+  *
+  * amcmds.c
+  *	  Routines for SQL commands that manipulate access methods.
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *
+  * IDENTIFICATION
+  *	  src/backend/commands/amcmds.c
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "access/heapam.h"
+ #include "access/htup_details.h"
+ #include "access/xact.h"
+ #include "catalog/binary_upgrade.h"
+ #include "catalog/catalog.h"
+ #include "catalog/dependency.h"
+ #include "catalog/heap.h"
+ #include "catalog/indexing.h"
+ #include "catalog/objectaccess.h"
+ #include "catalog/pg_authid.h"
+ #include "catalog/pg_am.h"
+ #include "catalog/pg_collation.h"
+ #include "catalog/pg_constraint.h"
+ #include "catalog/pg_depend.h"
+ #include "catalog/pg_enum.h"
+ #include "catalog/pg_language.h"
+ #include "catalog/pg_namespace.h"
+ #include "catalog/pg_proc.h"
+ #include "catalog/pg_proc_fn.h"
+ #include "catalog/pg_range.h"
+ #include "catalog/pg_type.h"
+ #include "catalog/pg_type_fn.h"
+ #include "commands/dbcommands.h"
+ #include "commands/defrem.h"
+ #include "commands/tablecmds.h"
+ #include "commands/typecmds.h"
+ #include "executor/executor.h"
+ #include "miscadmin.h"
+ #include "nodes/makefuncs.h"
+ #include "optimizer/planner.h"
+ #include "optimizer/var.h"
+ #include "parser/parse_coerce.h"
+ #include "parser/parse_collate.h"
+ #include "parser/parse_expr.h"
+ #include "parser/parse_func.h"
+ #include "parser/parse_type.h"
+ #include "utils/acl.h"
+ #include "utils/builtins.h"
+ #include "utils/fmgroids.h"
+ #include "utils/lsyscache.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ #include "utils/snapmgr.h"
+ #include "utils/syscache.h"
+ #include "utils/tqual.h"
+ 
+ /*
+  * Convert a handler function name passed from the parser to an Oid. This
+  * function either return valid function Oid or throw an error.
+  */
+ static Oid
+ lookup_index_am_handler_func(List *handler_name)
+ {
+ 	Oid			handlerOid;
+ 	Oid			funcargtypes[1] = {INTERNALOID};
+ 
+ 	if (handler_name == NIL)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
+ 				 errmsg("handler function is not specified")));
+ 
+ 	/* handlers have no arguments */
+ 	handlerOid = LookupFuncName(handler_name, 1, funcargtypes, false);
+ 
+ 	/* check that handler has correct return type */
+ 	if (get_func_rettype(handlerOid) != INDEX_AM_HANDLEROID)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 				 errmsg("function %s must return type \"index_am_handler\"",
+ 						NameListToString(handler_name))));
+ 
+ 	return handlerOid;
+ }
+ 
+ 
+ /*
+  * CreateAcessMethod
+  *		Registers a new access method.
+  */
+ ObjectAddress
+ CreateAccessMethod(CreateAmStmt *stmt)
+ {
+ 	Relation		rel;
+ 	ObjectAddress	myself;
+ 	ObjectAddress	referenced;
+ 	Oid				amoid;
+ 	Oid				amhandler;
+ 	bool			nulls[Natts_pg_am];
+ 	Datum			values[Natts_pg_am];
+ 	HeapTuple		tup;
+ 
+ 	rel = heap_open(AccessMethodRelationId, RowExclusiveLock);
+ 
+ 	/* Must be super user */
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 			errmsg("permission denied to create access method \"%s\"",
+ 				   stmt->amname),
+ 			errhint("Must be superuser to create access method.")));
+ 
+ 	/* Check if name is busy */
+ 	amoid = GetSysCacheOid1(AMNAME, CStringGetDatum(stmt->amname));
+ 	if (OidIsValid(amoid))
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_DUPLICATE_OBJECT),
+ 				 errmsg("access method \"%s\" already exists", stmt->amname)));
+ 	}
+ 
+ 	/*
+ 	 * Get handler function oid. Handler signature depends on access method
+ 	 * type.
+ 	 */
+ 	switch(stmt->amtype)
+ 	{
+ 		case AMTYPE_INDEX:
+ 			amhandler = lookup_index_am_handler_func(stmt->handler_name);
+ 			break;
+ 		default:
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 					 errmsg("wrong access method type \"%c\"", stmt->amtype)));
+ 			break;
+ 	}
+ 
+ 	/*
+ 	 * Insert tuple into pg_am.
+ 	 */
+ 	memset(values, 0, sizeof(values));
+ 	memset(nulls, false, sizeof(nulls));
+ 
+ 	values[Anum_pg_am_amname - 1] =
+ 		DirectFunctionCall1(namein, CStringGetDatum(stmt->amname));
+ 	values[Anum_pg_am_amhandler - 1] = ObjectIdGetDatum(amhandler);
+ 	values[Anum_pg_am_amtype - 1] = CharGetDatum(stmt->amtype);
+ 
+ 	tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+ 
+ 	amoid = simple_heap_insert(rel, tup);
+ 	CatalogUpdateIndexes(rel, tup);
+ 	heap_freetuple(tup);
+ 
+ 	myself.classId = AccessMethodRelationId;
+ 	myself.objectId = amoid;
+ 	myself.objectSubId = 0;
+ 
+ 	/* Record dependecy on handler function */
+ 	referenced.classId = ProcedureRelationId;
+ 	referenced.objectId = amhandler;
+ 	referenced.objectSubId = 0;
+ 
+ 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ 
+ 	recordDependencyOnCurrentExtension(&myself, false);
+ 
+ 	heap_close(rel, RowExclusiveLock);
+ 
+ 	return myself;
+ }
+ 
+ /*
+  * Guts of access method deletion.
+  */
+ void
+ RemoveAccessMethodById(Oid amOid)
+ {
+ 	Relation	relation;
+ 	HeapTuple	tup;
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 errmsg("must be superuser to drop access methods")));
+ 
+ 	relation = heap_open(AccessMethodRelationId, RowExclusiveLock);
+ 
+ 	tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
+ 	if (!HeapTupleIsValid(tup))
+ 		elog(ERROR, "cache lookup failed for access method %u", amOid);
+ 
+ 	simple_heap_delete(relation, &tup->t_self);
+ 
+ 	ReleaseSysCache(tup);
+ 
+ 	heap_close(relation, RowExclusiveLock);
+ }
+ 
+ /*
+  * Convert single charater access method type into string for error reporting.
+  */
+ static char *
+ get_am_type_string(char amtype)
+ {
+ 	switch (amtype)
+ 	{
+ 		case AMTYPE_INDEX:
+ 			return "index";
+ 		default:
+ 			elog(ERROR, "invalid access method type '%c'", amtype);
+ 	}
+ }
+ 
+ /*
+  * get_am_oid - given an access method name and type, look up the OID
+  *
+  * If missing_ok is false, throw an error if access method not found.  If
+  * true, just return InvalidOid.
+  */
+ Oid
+ get_am_oid(const char *amname, char amtype, bool missing_ok)
+ {
+ 	HeapTuple	tup;
+ 	Oid			oid = InvalidOid;
+ 
+ 	tup = SearchSysCache1(AMNAME, CStringGetDatum(amname));
+ 	if (HeapTupleIsValid(tup))
+ 	{
+ 		Form_pg_am amform = (Form_pg_am) GETSTRUCT(tup);
+ 
+ 		if (amform->amtype != amtype)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 					 errmsg("access method \"%s\" type is not %s",
+ 							NameStr(amform->amname),
+ 							get_am_type_string(amtype))));
+ 
+ 		oid = HeapTupleGetOid(tup);
+ 		ReleaseSysCache(tup);
+ 	}
+ 
+ 	if (!OidIsValid(oid) && !missing_ok)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 				 errmsg("access method \"%s\" does not exist", amname)));
+ 	return oid;
+ }
+ 
+ /*
+  * get_am_name - given an access method OID name and type, look up the name
+  *
+  * Access method type is not required for lookup.  However it's useful to check
+  * the type to ensure it is what we're looking for.
+  */
+ char *
+ get_am_name(Oid amOid)
+ {
+ 	HeapTuple	tup;
+ 	char	   *result = NULL;
+ 
+ 	tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
+ 	if (HeapTupleIsValid(tup))
+ 	{
+ 		Form_pg_am amform = (Form_pg_am) GETSTRUCT(tup);
+ 
+ 		result = pstrdup(NameStr(amform->amname));
+ 		ReleaseSysCache(tup);
+ 	}
+ 	return result;
+ }
+ 
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
new file mode 100644
index 9e32f8d..3f52ad8
*** a/src/backend/commands/event_trigger.c
--- b/src/backend/commands/event_trigger.c
*************** typedef enum
*** 86,91 ****
--- 86,92 ----
  
  /* XXX merge this with ObjectTypeMap? */
  static event_trigger_support_data event_trigger_support[] = {
+ 	{"ACCESS METHOD", true},
  	{"AGGREGATE", true},
  	{"CAST", true},
  	{"CONSTRAINT", true},
*************** EventTriggerSupportsObjectType(ObjectTyp
*** 1078,1083 ****
--- 1079,1085 ----
  		case OBJECT_EVENT_TRIGGER:
  			/* no support for event triggers on event triggers */
  			return false;
+ 		case OBJECT_ACCESS_METHOD:
  		case OBJECT_AGGREGATE:
  		case OBJECT_AMOP:
  		case OBJECT_AMPROC:
*************** EventTriggerSupportsObjectClass(ObjectCl
*** 1167,1172 ****
--- 1169,1175 ----
  		case OCLASS_DEFACL:
  		case OCLASS_EXTENSION:
  		case OCLASS_POLICY:
+ 		case OCLASS_AM:
  			return true;
  	}
  
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
new file mode 100644
index 8a66196..19dac98
*** a/src/backend/commands/opclasscmds.c
--- b/src/backend/commands/opclasscmds.c
*************** DefineOpClass(CreateOpClassStmt *stmt)
*** 678,683 ****
--- 678,689 ----
  	myself.objectId = opclassoid;
  	myself.objectSubId = 0;
  
+ 	/* dependency on access method */
+ 	referenced.classId = AccessMethodRelationId;
+ 	referenced.objectId = amoid;
+ 	referenced.objectSubId = 0;
+ 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ 
  	/* dependency on namespace */
  	referenced.classId = NamespaceRelationId;
  	referenced.objectId = namespaceoid;
*************** DefineOpFamily(CreateOpFamilyStmt *stmt)
*** 743,749 ****
  					   get_namespace_name(namespaceoid));
  
  	/* Get access method OID, throwing an error if it doesn't exist. */
! 	amoid = get_am_oid(stmt->amname, false);
  
  	/* XXX Should we make any privilege check against the AM? */
  
--- 749,755 ----
  					   get_namespace_name(namespaceoid));
  
  	/* Get access method OID, throwing an error if it doesn't exist. */
! 	amoid = get_am_oid(stmt->amname, AMTYPE_INDEX, false);
  
  	/* XXX Should we make any privilege check against the AM? */
  
*************** RemoveAmProcEntryById(Oid entryOid)
*** 1663,1683 ****
  	heap_close(rel, RowExclusiveLock);
  }
  
- char *
- get_am_name(Oid amOid)
- {
- 	HeapTuple	tup;
- 	char	   *result = NULL;
- 
- 	tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
- 	if (HeapTupleIsValid(tup))
- 	{
- 		result = pstrdup(NameStr(((Form_pg_am) GETSTRUCT(tup))->amname));
- 		ReleaseSysCache(tup);
- 	}
- 	return result;
- }
- 
  /*
   * Subroutine for ALTER OPERATOR CLASS SET SCHEMA/RENAME
   *
--- 1669,1674 ----
*************** IsThereOpFamilyInNamespace(const char *o
*** 1723,1744 ****
  						get_am_name(opfmethod),
  						get_namespace_name(opfnamespace))));
  }
- 
- /*
-  * get_am_oid - given an access method name, look up the OID
-  *
-  * If missing_ok is false, throw an error if access method not found.  If
-  * true, just return InvalidOid.
-  */
- Oid
- get_am_oid(const char *amname, bool missing_ok)
- {
- 	Oid			oid;
- 
- 	oid = GetSysCacheOid1(AMNAME, CStringGetDatum(amname));
- 	if (!OidIsValid(oid) && !missing_ok)
- 		ereport(ERROR,
- 				(errcode(ERRCODE_UNDEFINED_OBJECT),
- 				 errmsg("access method \"%s\" does not exist", amname)));
- 	return oid;
- }
--- 1714,1716 ----
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
new file mode 100644
index df7c2fa..37529e1
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copyCreateTransformStmt(const CreateTra
*** 3828,3833 ****
--- 3828,3845 ----
  	return newnode;
  }
  
+ static CreateAmStmt *
+ _copyCreateAmStmt(const CreateAmStmt *from)
+ {
+ 	CreateAmStmt *newnode = makeNode(CreateAmStmt);
+ 
+ 	COPY_STRING_FIELD(amname);
+ 	COPY_NODE_FIELD(handler_name);
+ 	COPY_SCALAR_FIELD(amtype);
+ 
+ 	return newnode;
+ }
+ 
  static CreateTrigStmt *
  _copyCreateTrigStmt(const CreateTrigStmt *from)
  {
*************** copyObject(const void *from)
*** 4819,4824 ****
--- 4831,4839 ----
  		case T_CreateTransformStmt:
  			retval = _copyCreateTransformStmt(from);
  			break;
+ 		case T_CreateAmStmt:
+ 			retval = _copyCreateAmStmt(from);
+ 			break;
  		case T_CreateTrigStmt:
  			retval = _copyCreateTrigStmt(from);
  			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
new file mode 100644
index b9c3959..f19fde1
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
*************** _equalCreateTransformStmt(const CreateTr
*** 1855,1860 ****
--- 1855,1870 ----
  }
  
  static bool
+ _equalCreateAmStmt(const CreateAmStmt *a, const CreateAmStmt *b)
+ {
+ 	COMPARE_STRING_FIELD(amname);
+ 	COMPARE_NODE_FIELD(handler_name);
+ 	COMPARE_SCALAR_FIELD(amtype);
+ 
+ 	return true;
+ }
+ 
+ static bool
  _equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b)
  {
  	COMPARE_STRING_FIELD(trigname);
*************** equal(const void *a, const void *b)
*** 3146,3151 ****
--- 3156,3164 ----
  		case T_CreateTransformStmt:
  			retval = _equalCreateTransformStmt(a, b);
  			break;
+ 		case T_CreateAmStmt:
+ 			retval = _equalCreateAmStmt(a, b);
+ 			break;
  		case T_CreateTrigStmt:
  			retval = _equalCreateTrigStmt(a, b);
  			break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
new file mode 100644
index b307b48..ce541c4
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 51,56 ****
--- 51,57 ----
  
  #include "catalog/index.h"
  #include "catalog/namespace.h"
+ #include "catalog/pg_am.h"
  #include "catalog/pg_trigger.h"
  #include "commands/defrem.h"
  #include "commands/trigger.h"
*************** static Node *makeRecursiveViewSelect(cha
*** 263,269 ****
  		DeallocateStmt PrepareStmt ExecuteStmt
  		DropOwnedStmt ReassignOwnedStmt
  		AlterTSConfigurationStmt AlterTSDictionaryStmt
! 		CreateMatViewStmt RefreshMatViewStmt
  
  %type <node>	select_no_parens select_with_parens select_clause
  				simple_select values_clause
--- 264,270 ----
  		DeallocateStmt PrepareStmt ExecuteStmt
  		DropOwnedStmt ReassignOwnedStmt
  		AlterTSConfigurationStmt AlterTSDictionaryStmt
! 		CreateMatViewStmt RefreshMatViewStmt CreateAmStmt DropAmStmt
  
  %type <node>	select_no_parens select_with_parens select_clause
  				simple_select values_clause
*************** static Node *makeRecursiveViewSelect(cha
*** 604,610 ****
  	LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
  	LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
  
! 	MAPPING MATCH MATERIALIZED MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
  
  	NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NONE
  	NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
--- 605,611 ----
  	LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
  	LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
  
! 	MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE
  
  	NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NONE
  	NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
*************** stmt :
*** 789,794 ****
--- 790,796 ----
  			| CommentStmt
  			| ConstraintsSetStmt
  			| CopyStmt
+ 			| CreateAmStmt
  			| CreateAsStmt
  			| CreateAssertStmt
  			| CreateCastStmt
*************** stmt :
*** 823,828 ****
--- 825,831 ----
  			| DeleteStmt
  			| DiscardStmt
  			| DoStmt
+ 			| DropAmStmt
  			| DropAssertStmt
  			| DropCastStmt
  			| DropFdwStmt
*************** row_security_cmd:
*** 4708,4713 ****
--- 4711,4765 ----
  
  /*****************************************************************************
   *
+  *		QUERY:
+  *             CREATE ACCESS METHOD name HANDLER handler_name
+  *
+  *****************************************************************************/
+ 
+ CreateAmStmt: CREATE ACCESS METHOD name TYPE_P INDEX HANDLER handler_name
+ 				{
+ 					CreateAmStmt *n = makeNode(CreateAmStmt);
+ 					n->amname = $4;
+ 					n->handler_name = $8;
+ 					n->amtype = AMTYPE_INDEX;
+ 					$$ = (Node *) n;
+ 				}
+ 		;
+ 
+ /*****************************************************************************
+  *
+  *		QUERY :
+  *				DROP ACCESS METHOD name
+  *
+  ****************************************************************************/
+ 
+ DropAmStmt: DROP ACCESS METHOD name opt_drop_behavior
+ 				{
+ 					DropStmt *n = makeNode(DropStmt);
+ 					n->removeType = OBJECT_ACCESS_METHOD;
+ 					n->objects = list_make1(list_make1(makeString($4)));
+ 					n->arguments = NIL;
+ 					n->missing_ok = false;
+ 					n->behavior = $5;
+ 					n->concurrent = false;
+ 					$$ = (Node *) n;
+ 				}
+ 				|  DROP ACCESS METHOD IF_P EXISTS name opt_drop_behavior
+ 				{
+ 					DropStmt *n = makeNode(DropStmt);
+ 					n->removeType = OBJECT_ACCESS_METHOD;
+ 					n->objects = list_make1(list_make1(makeString($6)));
+ 					n->arguments = NIL;
+ 					n->missing_ok = true;
+ 					n->behavior = $7;
+ 					n->concurrent = false;
+ 					$$ = (Node *) n;
+ 				}
+ 		;
+ 
+ 
+ /*****************************************************************************
+  *
   *		QUERIES :
   *				CREATE TRIGGER ...
   *				DROP TRIGGER ...
*************** unreserved_keyword:
*** 13778,13783 ****
--- 13830,13836 ----
  			| MATCH
  			| MATERIALIZED
  			| MAXVALUE
+ 			| METHOD
  			| MINUTE_P
  			| MINVALUE
  			| MODE
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
new file mode 100644
index dc431c7..92d1baa
*** a/src/backend/parser/parse_utilcmd.c
--- b/src/backend/parser/parse_utilcmd.c
*************** transformIndexConstraint(Constraint *con
*** 1709,1715 ****
  		 * else dump and reload will produce a different index (breaking
  		 * pg_upgrade in particular).
  		 */
! 		if (index_rel->rd_rel->relam != get_am_oid(DEFAULT_INDEX_TYPE, false))
  			ereport(ERROR,
  					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
  					 errmsg("index \"%s\" is not a btree", index_name),
--- 1709,1715 ----
  		 * else dump and reload will produce a different index (breaking
  		 * pg_upgrade in particular).
  		 */
! 		if (index_rel->rd_rel->relam != get_am_oid(DEFAULT_INDEX_TYPE, AMTYPE_INDEX, false))
  			ereport(ERROR,
  					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
  					 errmsg("index \"%s\" is not a btree", index_name),
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
new file mode 100644
index 045f7f0..4d0aac9
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
*************** ProcessUtilitySlow(Node *parsetree,
*** 1520,1525 ****
--- 1520,1529 ----
  				address = ExecSecLabelStmt((SecLabelStmt *) parsetree);
  				break;
  
+ 			case T_CreateAmStmt:
+ 				address = CreateAccessMethod((CreateAmStmt *) parsetree);
+ 				break;
+ 
  			default:
  				elog(ERROR, "unrecognized node type: %d",
  					 (int) nodeTag(parsetree));
*************** CreateCommandTag(Node *parsetree)
*** 2160,2165 ****
--- 2164,2172 ----
  				case OBJECT_TRANSFORM:
  					tag = "DROP TRANSFORM";
  					break;
+ 				case OBJECT_ACCESS_METHOD:
+ 					tag = "DROP ACCESS METHOD";
+ 					break;
  				default:
  					tag = "???";
  			}
*************** CreateCommandTag(Node *parsetree)
*** 2256,2261 ****
--- 2263,2271 ----
  				case OBJECT_COLLATION:
  					tag = "CREATE COLLATION";
  					break;
+ 				case OBJECT_ACCESS_METHOD:
+ 					tag = "CREATE ACCESS METHOD";
+ 					break;
  				default:
  					tag = "???";
  			}
*************** CreateCommandTag(Node *parsetree)
*** 2519,2524 ****
--- 2529,2538 ----
  			tag = "ALTER POLICY";
  			break;
  
+ 		case T_CreateAmStmt:
+ 			tag = "CREATE ACCESS METHOD";
+ 			break;
+ 
  		case T_PrepareStmt:
  			tag = "PREPARE";
  			break;
*************** GetCommandLogLevel(Node *parsetree)
*** 3076,3081 ****
--- 3090,3099 ----
  			lev = LOGSTMT_DDL;
  			break;
  
+ 		case T_CreateAmStmt:
+ 			lev = LOGSTMT_DDL;
+ 			break;
+ 
  			/* already-planned queries */
  		case T_PlannedStmt:
  			{
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
new file mode 100644
index 46c95b0..516af92
*** a/src/backend/utils/adt/selfuncs.c
--- b/src/backend/utils/adt/selfuncs.c
*************** string_to_bytea_const(const char *str, s
*** 6012,6032 ****
   *-------------------------------------------------------------------------
   */
  
! /*
!  * deconstruct_indexquals is a simple function to examine the indexquals
!  * attached to a proposed IndexPath.  It returns a list of IndexQualInfo
!  * structs, one per qual expression.
!  */
! typedef struct
! {
! 	RestrictInfo *rinfo;		/* the indexqual itself */
! 	int			indexcol;		/* zero-based index column number */
! 	bool		varonleft;		/* true if index column is on left of qual */
! 	Oid			clause_op;		/* qual's operator OID, if relevant */
! 	Node	   *other_operand;	/* non-index operand of qual's operator */
! } IndexQualInfo;
! 
! static List *
  deconstruct_indexquals(IndexPath *path)
  {
  	List	   *result = NIL;
--- 6012,6018 ----
   *-------------------------------------------------------------------------
   */
  
! List *
  deconstruct_indexquals(IndexPath *path)
  {
  	List	   *result = NIL;
*************** orderby_operands_eval_cost(PlannerInfo *
*** 6176,6210 ****
  	return qual_arg_cost;
  }
  
! /*
!  * genericcostestimate is a general-purpose estimator that can be used for
!  * most index types.  In some cases we use genericcostestimate as the base
!  * code and then incorporate additional index-type-specific knowledge in
!  * the type-specific calling function.  To avoid code duplication, we make
!  * genericcostestimate return a number of intermediate values as well as
!  * its preliminary estimates of the output cost values.  The GenericCosts
!  * struct includes all these values.
!  *
!  * Callers should initialize all fields of GenericCosts to zero.  In addition,
!  * they can set numIndexTuples to some positive value if they have a better
!  * than default way of estimating the number of leaf index tuples visited.
!  */
! typedef struct
! {
! 	/* These are the values the cost estimator must return to the planner */
! 	Cost		indexStartupCost;		/* index-related startup cost */
! 	Cost		indexTotalCost; /* total index-related scan cost */
! 	Selectivity indexSelectivity;		/* selectivity of index */
! 	double		indexCorrelation;		/* order correlation of index */
! 
! 	/* Intermediate values we obtain along the way */
! 	double		numIndexPages;	/* number of leaf pages visited */
! 	double		numIndexTuples; /* number of leaf tuples visited */
! 	double		spc_random_page_cost;	/* relevant random_page_cost value */
! 	double		num_sa_scans;	/* # indexscans from ScalarArrayOps */
! } GenericCosts;
! 
! static void
  genericcostestimate(PlannerInfo *root,
  					IndexPath *path,
  					double loop_count,
--- 6162,6168 ----
  	return qual_arg_cost;
  }
  
! void
  genericcostestimate(PlannerInfo *root,
  					IndexPath *path,
  					double loop_count,
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
new file mode 100644
index f798b15..1acd91a
*** a/src/bin/pg_dump/common.c
--- b/src/bin/pg_dump/common.c
*************** getSchemaData(Archive *fout, int *numTab
*** 98,103 ****
--- 98,104 ----
  	int			numProcLangs;
  	int			numCasts;
  	int			numTransforms;
+ 	int			numAccessMethods;
  	int			numOpclasses;
  	int			numOpfamilies;
  	int			numConversions;
*************** getSchemaData(Archive *fout, int *numTab
*** 169,174 ****
--- 170,179 ----
  	oprinfoindex = buildIndexArray(oprinfo, numOperators, sizeof(OprInfo));
  
  	if (g_verbose)
+ 		write_msg(NULL, "reading user-defined access methods\n");
+ 	getAccessMethods(fout, &numAccessMethods);
+ 
+ 	if (g_verbose)
  		write_msg(NULL, "reading user-defined operator classes\n");
  	getOpclasses(fout, &numOpclasses);
  
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
new file mode 100644
index 64c2673..9061afa
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 45,50 ****
--- 45,51 ----
  #include "access/attnum.h"
  #include "access/sysattr.h"
  #include "access/transam.h"
+ #include "catalog/pg_am.h"
  #include "catalog/pg_cast.h"
  #include "catalog/pg_class.h"
  #include "catalog/pg_default_acl.h"
*************** static void dumpFunc(Archive *fout, Func
*** 173,178 ****
--- 174,180 ----
  static void dumpCast(Archive *fout, CastInfo *cast);
  static void dumpTransform(Archive *fout, TransformInfo *transform);
  static void dumpOpr(Archive *fout, OprInfo *oprinfo);
+ static void dumpAccessMethod(Archive *fout, AccessMethodInfo *oprinfo);
  static void dumpOpclass(Archive *fout, OpclassInfo *opcinfo);
  static void dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo);
  static void dumpCollation(Archive *fout, CollInfo *convinfo);
*************** getConversions(Archive *fout, int *numCo
*** 4101,4106 ****
--- 4103,4187 ----
  }
  
  /*
+  * getAccessMethods:
+  *	  read all user-defined access methods in the system catalogs and return
+  *    them in the AccessMethodInfo* structure
+  *
+  *	numAccessMethods is set to the number of access methods read in
+  */
+ AccessMethodInfo *
+ getAccessMethods(Archive *fout, int *numAccessMethods)
+ {
+ 	DumpOptions *dopt = fout->dopt;
+ 	PGresult   *res;
+ 	int			ntups;
+ 	int			i;
+ 	PQExpBuffer query;
+ 	AccessMethodInfo *aminfo;
+ 	int			i_tableoid;
+ 	int			i_oid;
+ 	int			i_amname;
+ 	int			i_amhandler;
+ 	int			i_amtype;
+ 
+ 	/* Before 9.6, there are no user-defined access methods */
+ 	if (fout->remoteVersion < 90600)
+ 	{
+ 		*numAccessMethods = 0;
+ 		return NULL;
+ 	}
+ 
+ 	query = createPQExpBuffer();
+ 
+ 	/* Make sure we are in proper schema */
+ 	selectSourceSchema(fout, "pg_catalog");
+ 
+ 	/*
+ 	 * Select only user-defined access methods assuming all built-in access
+ 	 * methods have oid < 10000.
+ 	 */
+ 	appendPQExpBuffer(query, "SELECT tableoid, oid, amname, amtype, "
+ 					  "amhandler::pg_catalog.regproc AS amhandler "
+ 					  "FROM pg_am "
+ 					  "WHERE oid >= 10000");
+ 
+ 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+ 
+ 	ntups = PQntuples(res);
+ 	*numAccessMethods = ntups;
+ 
+ 	aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
+ 
+ 	i_tableoid = PQfnumber(res, "tableoid");
+ 	i_oid = PQfnumber(res, "oid");
+ 	i_amname = PQfnumber(res, "amname");
+ 	i_amhandler = PQfnumber(res, "amhandler");
+ 	i_amtype = PQfnumber(res, "amtype");
+ 
+ 	for (i = 0; i < ntups; i++)
+ 	{
+ 		aminfo[i].dobj.objType = DO_ACCESS_METHOD;
+ 		aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ 		aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ 		AssignDumpId(&aminfo[i].dobj);
+ 		aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
+ 		aminfo[i].dobj.namespace = NULL;
+ 		aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
+ 		aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
+ 
+ 		/* Decide whether we want to dump it */
+ 		selectDumpableObject(&(aminfo[i].dobj), dopt);
+ 	}
+ 
+ 	PQclear(res);
+ 
+ 	destroyPQExpBuffer(query);
+ 
+ 	return aminfo;
+ }
+ 
+ 
+ /*
   * getOpclasses:
   *	  read all opclasses in the system catalogs and return them in the
   * OpclassInfo* structure
*************** dumpDumpableObject(Archive *fout, Dumpab
*** 8408,8413 ****
--- 8489,8497 ----
  		case DO_OPERATOR:
  			dumpOpr(fout, (OprInfo *) dobj);
  			break;
+ 		case DO_ACCESS_METHOD:
+ 			dumpAccessMethod(fout, (AccessMethodInfo *) dobj);
+ 			break;
  		case DO_OPCLASS:
  			dumpOpclass(fout, (OpclassInfo *) dobj);
  			break;
*************** convertTSFunction(Archive *fout, Oid fun
*** 11446,11451 ****
--- 11530,11603 ----
  	return result;
  }
  
+ /*
+  * dumpAccessMethod
+  *	  write out a single access method definition
+  */
+ static void
+ dumpAccessMethod(Archive *fout, AccessMethodInfo *aminfo)
+ {
+ 	DumpOptions *dopt = fout->dopt;
+ 	PQExpBuffer q;
+ 	PQExpBuffer delq;
+ 	PQExpBuffer labelq;
+ 	char	   *qamname;
+ 
+ 	/* Skip if not to be dumped */
+ 	if (!aminfo->dobj.dump || dopt->dataOnly)
+ 		return;
+ 
+ 	q = createPQExpBuffer();
+ 	delq = createPQExpBuffer();
+ 	labelq = createPQExpBuffer();
+ 
+ 	qamname = pg_strdup(fmtId(aminfo->dobj.name));
+ 
+ 	appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
+ 
+ 	switch (aminfo->amtype)
+ 	{
+ 		case AMTYPE_INDEX:
+ 			appendPQExpBuffer(q, "TYPE INDEX ");
+ 			break;
+ 		default:
+ 			write_msg(NULL, "WARNING: invalid type %c of access method %s\n",
+ 					  aminfo->amtype, qamname);
+ 			destroyPQExpBuffer(q);
+ 			destroyPQExpBuffer(delq);
+ 			destroyPQExpBuffer(labelq);
+ 			return;
+ 	}
+ 
+ 	appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
+ 
+ 	appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
+ 					  qamname);
+ 
+ 	appendPQExpBuffer(labelq, "ACCESS METHOD %s",
+ 					  qamname);
+ 
+ 	ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
+ 				 aminfo->dobj.name,
+ 				 NULL,
+ 				 NULL,
+ 				 "",
+ 				 false, "ACCESS METHOD", SECTION_PRE_DATA,
+ 				 q->data, delq->data, NULL,
+ 				 NULL, 0,
+ 				 NULL, NULL);
+ 
+ 	/* Dump Access Method Comments */
+ 	dumpComment(fout, labelq->data,
+ 				NULL, "",
+ 				aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
+ 
+ 	free(qamname);
+ 
+ 	destroyPQExpBuffer(q);
+ 	destroyPQExpBuffer(delq);
+ 	destroyPQExpBuffer(labelq);
+ }
  
  /*
   * dumpOpclass
*************** addBoundaryDependencies(DumpableObject *
*** 16227,16232 ****
--- 16379,16385 ----
  			case DO_FUNC:
  			case DO_AGG:
  			case DO_OPERATOR:
+ 			case DO_ACCESS_METHOD:
  			case DO_OPCLASS:
  			case DO_OPFAMILY:
  			case DO_COLLATION:
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
new file mode 100644
index 9a1d8f8..66e6931
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
*************** typedef enum
*** 48,53 ****
--- 48,54 ----
  	DO_FUNC,
  	DO_AGG,
  	DO_OPERATOR,
+ 	DO_ACCESS_METHOD,
  	DO_OPCLASS,
  	DO_OPFAMILY,
  	DO_COLLATION,
*************** typedef struct _oprInfo
*** 167,172 ****
--- 168,180 ----
  	Oid			oprcode;
  } OprInfo;
  
+ typedef struct _accessMethodInfo
+ {
+ 	DumpableObject dobj;
+ 	char		amtype;
+ 	char	   *amhandler;
+ } AccessMethodInfo;
+ 
  typedef struct _opclassInfo
  {
  	DumpableObject dobj;
*************** extern TypeInfo *getTypes(Archive *fout,
*** 548,553 ****
--- 556,562 ----
  extern FuncInfo *getFuncs(Archive *fout, int *numFuncs);
  extern AggInfo *getAggregates(Archive *fout, int *numAggregates);
  extern OprInfo *getOperators(Archive *fout, int *numOperators);
+ extern AccessMethodInfo *getAccessMethods(Archive *fout, int *numAccessMethods);
  extern OpclassInfo *getOpclasses(Archive *fout, int *numOpclasses);
  extern OpfamilyInfo *getOpfamilies(Archive *fout, int *numOpfamilies);
  extern CollInfo *getCollations(Archive *fout, int *numCollations);
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
new file mode 100644
index 78ff59c..52d5625
*** a/src/bin/pg_dump/pg_dump_sort.c
--- b/src/bin/pg_dump/pg_dump_sort.c
*************** static const int oldObjectTypePriority[]
*** 45,50 ****
--- 45,51 ----
  	2,							/* DO_FUNC */
  	3,							/* DO_AGG */
  	3,							/* DO_OPERATOR */
+ 	3,							/* DO_ACCESS_METHOD */
  	4,							/* DO_OPCLASS */
  	4,							/* DO_OPFAMILY */
  	4,							/* DO_COLLATION */
*************** static const int newObjectTypePriority[]
*** 95,100 ****
--- 96,102 ----
  	6,							/* DO_FUNC */
  	7,							/* DO_AGG */
  	8,							/* DO_OPERATOR */
+ 	8,							/* DO_ACCESS_METHOD */
  	9,							/* DO_OPCLASS */
  	9,							/* DO_OPFAMILY */
  	3,							/* DO_COLLATION */
*************** describeDumpableObject(DumpableObject *o
*** 1329,1334 ****
--- 1331,1341 ----
  					 "OPERATOR %s  (ID %d OID %u)",
  					 obj->name, obj->dumpId, obj->catId.oid);
  			return;
+ 		case DO_ACCESS_METHOD:
+ 			snprintf(buf, bufsize,
+ 					 "ACCESS METHOD %s  (ID %d OID %u)",
+ 					 obj->name, obj->dumpId, obj->catId.oid);
+ 			return;
  		case DO_OPCLASS:
  			snprintf(buf, bufsize,
  					 "OPERATOR CLASS %s  (ID %d OID %u)",
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
new file mode 100644
index 049bf9f..ac16740
*** a/src/include/catalog/dependency.h
--- b/src/include/catalog/dependency.h
*************** typedef enum ObjectClass
*** 153,162 ****
  	OCLASS_EXTENSION,			/* pg_extension */
  	OCLASS_EVENT_TRIGGER,		/* pg_event_trigger */
  	OCLASS_POLICY,				/* pg_policy */
! 	OCLASS_TRANSFORM			/* pg_transform */
  } ObjectClass;
  
! #define LAST_OCLASS		OCLASS_TRANSFORM
  
  
  /* in dependency.c */
--- 153,163 ----
  	OCLASS_EXTENSION,			/* pg_extension */
  	OCLASS_EVENT_TRIGGER,		/* pg_event_trigger */
  	OCLASS_POLICY,				/* pg_policy */
! 	OCLASS_TRANSFORM,			/* pg_transform */
! 	OCLASS_AM,					/* pg_am */
  } ObjectClass;
  
! #define LAST_OCLASS		OCLASS_AM
  
  
  /* in dependency.c */
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
new file mode 100644
index f801c3e..6fba1cd
*** a/src/include/catalog/pg_am.h
--- b/src/include/catalog/pg_am.h
*************** CATALOG(pg_am,2601)
*** 35,40 ****
--- 35,41 ----
  {
  	NameData	amname;			/* access method name */
  	regproc		amhandler;		/* handler function */
+ 	char		amtype;			/* see AMTYPE_xxx constants below */
  } FormData_pg_am;
  
  /* ----------------
*************** typedef FormData_pg_am *Form_pg_am;
*** 48,78 ****
   *		compiler constants for pg_am
   * ----------------
   */
! #define Natts_pg_am						2
  #define Anum_pg_am_amname				1
  #define Anum_pg_am_amhandler			2
  
  /* ----------------
   *		initial contents of pg_am
   * ----------------
   */
  
! DATA(insert OID = 403 (  btree		bthandler ));
  DESCR("b-tree index access method");
  #define BTREE_AM_OID 403
! DATA(insert OID = 405 (  hash		hashhandler ));
  DESCR("hash index access method");
  #define HASH_AM_OID 405
! DATA(insert OID = 783 (  gist		gisthandler ));
  DESCR("GiST index access method");
  #define GIST_AM_OID 783
! DATA(insert OID = 2742 (  gin		ginhandler ));
  DESCR("GIN index access method");
  #define GIN_AM_OID 2742
! DATA(insert OID = 4000 (  spgist	spghandler ));
  DESCR("SP-GiST index access method");
  #define SPGIST_AM_OID 4000
! DATA(insert OID = 3580 (  brin		brinhandler ));
  DESCR("block range index (BRIN) access method");
  #define BRIN_AM_OID 3580
  
--- 49,86 ----
   *		compiler constants for pg_am
   * ----------------
   */
! #define Natts_pg_am						3
  #define Anum_pg_am_amname				1
  #define Anum_pg_am_amhandler			2
+ #define Anum_pg_am_amtype				3
+ 
+ /* ----------------
+  *		compiler constant for amtype
+  * ----------------
+  */
+ #define AMTYPE_INDEX					'i'		/* index access method */
  
  /* ----------------
   *		initial contents of pg_am
   * ----------------
   */
  
! DATA(insert OID = 403 (  btree		bthandler	i ));
  DESCR("b-tree index access method");
  #define BTREE_AM_OID 403
! DATA(insert OID = 405 (  hash		hashhandler	i ));
  DESCR("hash index access method");
  #define HASH_AM_OID 405
! DATA(insert OID = 783 (  gist		gisthandler	i ));
  DESCR("GiST index access method");
  #define GIST_AM_OID 783
! DATA(insert OID = 2742 (  gin		ginhandler	i ));
  DESCR("GIN index access method");
  #define GIN_AM_OID 2742
! DATA(insert OID = 4000 (  spgist	spghandler	i ));
  DESCR("SP-GiST index access method");
  #define SPGIST_AM_OID 4000
! DATA(insert OID = 3580 (  brin		brinhandler	i ));
  DESCR("block range index (BRIN) access method");
  #define BRIN_AM_OID 3580
  
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
new file mode 100644
index 54f67e9..1e21f03
*** a/src/include/commands/defrem.h
--- b/src/include/commands/defrem.h
*************** extern void IsThereOpClassInNamespace(co
*** 91,98 ****
  						  Oid opcnamespace);
  extern void IsThereOpFamilyInNamespace(const char *opfname, Oid opfmethod,
  						   Oid opfnamespace);
- extern Oid	get_am_oid(const char *amname, bool missing_ok);
- extern char *get_am_name(Oid amOid);
  extern Oid	get_opclass_oid(Oid amID, List *opclassname, bool missing_ok);
  extern Oid	get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok);
  
--- 91,96 ----
*************** extern Datum transformGenericOptions(Oid
*** 137,142 ****
--- 135,146 ----
  						List *options,
  						Oid fdwvalidator);
  
+ /* commands/amcmds.c */
+ extern ObjectAddress CreateAccessMethod(CreateAmStmt *stmt);
+ extern void RemoveAccessMethodById(Oid amOid);
+ extern Oid	get_am_oid(const char *amname, char amtype, bool missing_ok);
+ extern char *get_am_name(Oid amOid);
+ 
  /* support routines in commands/define.c */
  
  extern char *defGetString(DefElem *def);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
new file mode 100644
index fad9988..8cd1d89
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
*************** typedef enum NodeTag
*** 401,406 ****
--- 401,407 ----
  	T_CreatePolicyStmt,
  	T_AlterPolicyStmt,
  	T_CreateTransformStmt,
+ 	T_CreateAmStmt,
  
  	/*
  	 * TAGS FOR PARSE TREE NODES (parsenodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
new file mode 100644
index 2fd0629..8b958b4
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
*************** typedef struct SetOperationStmt
*** 1379,1384 ****
--- 1379,1385 ----
  
  typedef enum ObjectType
  {
+ 	OBJECT_ACCESS_METHOD,
  	OBJECT_AGGREGATE,
  	OBJECT_AMOP,
  	OBJECT_AMPROC,
*************** typedef struct AlterPolicyStmt
*** 2070,2075 ****
--- 2071,2088 ----
  	Node	   *with_check;		/* the policy's WITH CHECK condition. */
  } AlterPolicyStmt;
  
+ /*----------------------
+  *		Create ACCESS METHOD Statement
+  *----------------------
+  */
+ typedef struct CreateAmStmt
+ {
+ 	NodeTag		type;
+ 	char	   *amname;			/* access method name */
+ 	List	   *handler_name;	/* handler function name */
+ 	char		amtype;			/* type of access method */
+ } CreateAmStmt;
+ 
  /* ----------------------
   *		Create TRIGGER Statement
   * ----------------------
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
new file mode 100644
index 6e1e820..7de3404
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
*************** PG_KEYWORD("mapping", MAPPING, UNRESERVE
*** 239,244 ****
--- 239,245 ----
  PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD)
  PG_KEYWORD("materialized", MATERIALIZED, UNRESERVED_KEYWORD)
  PG_KEYWORD("maxvalue", MAXVALUE, UNRESERVED_KEYWORD)
+ PG_KEYWORD("method", METHOD, UNRESERVED_KEYWORD)
  PG_KEYWORD("minute", MINUTE_P, UNRESERVED_KEYWORD)
  PG_KEYWORD("minvalue", MINVALUE, UNRESERVED_KEYWORD)
  PG_KEYWORD("mode", MODE, UNRESERVED_KEYWORD)
diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h
new file mode 100644
index 06fbca7..7fb7466
*** a/src/include/utils/selfuncs.h
--- b/src/include/utils/selfuncs.h
*************** typedef enum
*** 95,100 ****
--- 95,142 ----
  	Pattern_Prefix_None, Pattern_Prefix_Partial, Pattern_Prefix_Exact
  } Pattern_Prefix_Status;
  
+ /*
+  * deconstruct_indexquals is a simple function to examine the indexquals
+  * attached to a proposed IndexPath.  It returns a list of IndexQualInfo
+  * structs, one per qual expression.
+  */
+ typedef struct
+ {
+ 	RestrictInfo *rinfo;		/* the indexqual itself */
+ 	int			indexcol;		/* zero-based index column number */
+ 	bool		varonleft;		/* true if index column is on left of qual */
+ 	Oid			clause_op;		/* qual's operator OID, if relevant */
+ 	Node	   *other_operand;	/* non-index operand of qual's operator */
+ } IndexQualInfo;
+ 
+ /*
+  * genericcostestimate is a general-purpose estimator that can be used for
+  * most index types.  In some cases we use genericcostestimate as the base
+  * code and then incorporate additional index-type-specific knowledge in
+  * the type-specific calling function.  To avoid code duplication, we make
+  * genericcostestimate return a number of intermediate values as well as
+  * its preliminary estimates of the output cost values.  The GenericCosts
+  * struct includes all these values.
+  *
+  * Callers should initialize all fields of GenericCosts to zero.  In addition,
+  * they can set numIndexTuples to some positive value if they have a better
+  * than default way of estimating the number of leaf index tuples visited.
+  */
+ typedef struct
+ {
+ 	/* These are the values the cost estimator must return to the planner */
+ 	Cost		indexStartupCost;		/* index-related startup cost */
+ 	Cost		indexTotalCost; /* total index-related scan cost */
+ 	Selectivity indexSelectivity;		/* selectivity of index */
+ 	double		indexCorrelation;		/* order correlation of index */
+ 
+ 	/* Intermediate values we obtain along the way */
+ 	double		numIndexPages;	/* number of leaf pages visited */
+ 	double		numIndexTuples; /* number of leaf tuples visited */
+ 	double		spc_random_page_cost;	/* relevant random_page_cost value */
+ 	double		num_sa_scans;	/* # indexscans from ScalarArrayOps */
+ } GenericCosts;
+ 
  /* Hooks for plugins to get control when we ask for stats */
  typedef bool (*get_relation_stats_hook_type) (PlannerInfo *root,
  														  RangeTblEntry *rte,
*************** extern double estimate_num_groups(Planne
*** 191,196 ****
--- 233,244 ----
  extern Selectivity estimate_hash_bucketsize(PlannerInfo *root, Node *hashkey,
  						 double nbuckets);
  
+ extern List *deconstruct_indexquals(IndexPath *path);
+ extern void genericcostestimate(PlannerInfo *root, IndexPath *path,
+ 								double loop_count,
+ 								List *qinfos,
+ 								GenericCosts *costs);
+ 
  /* Functions in array_selfuncs.c */
  
  extern Selectivity scalararraysel_containment(PlannerInfo *root,
diff --git a/src/test/regress/expected/create_am.out b/src/test/regress/expected/create_am.out
new file mode 100644
index ...47d6024
*** a/src/test/regress/expected/create_am.out
--- b/src/test/regress/expected/create_am.out
***************
*** 0 ****
--- 1,108 ----
+ --
+ -- Create access method tests
+ --
+ -- Make gist2 over gisthandler. In fact, it would be a synonym to gist.
+ CREATE ACCESS METHOD gist2 TYPE INDEX HANDLER gisthandler;
+ -- Drop old index on fast_emp4000
+ DROP INDEX grect2ind;
+ -- Try to create gist2 index on fast_emp4000: fail because opclass doesn't exist
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ ERROR:  data type box has no default operator class for access method "gist2"
+ HINT:  You must specify an operator class for the index or define a default operator class for the data type.
+ -- Make operator class for boxes using gist2
+ CREATE OPERATOR CLASS box_ops DEFAULT
+ 	FOR TYPE box USING gist2 AS
+ 	OPERATOR 1	<<,
+ 	OPERATOR 2	&<,
+ 	OPERATOR 3	&&,
+ 	OPERATOR 4	&>,
+ 	OPERATOR 5	>>,
+ 	OPERATOR 6	~=,
+ 	OPERATOR 7	@>,
+ 	OPERATOR 8	<@,
+ 	OPERATOR 9	&<|,
+ 	OPERATOR 10	<<|,
+ 	OPERATOR 11	|>>,
+ 	OPERATOR 12	|&>,
+ 	OPERATOR 13	~,
+ 	OPERATOR 14	@,
+ 	FUNCTION 1	gist_box_consistent(internal, box, smallint, oid, internal),
+ 	FUNCTION 2	gist_box_union(internal, internal),
+ 	FUNCTION 3	gist_box_compress(internal),
+ 	FUNCTION 4	gist_box_decompress(internal),
+ 	FUNCTION 5	gist_box_penalty(internal, internal, internal),
+ 	FUNCTION 6	gist_box_picksplit(internal, internal),
+ 	FUNCTION 7	gist_box_same(box, box, internal),
+ 	FUNCTION 9	gist_box_fetch(internal);
+ -- Create gist2 index on fast_emp4000
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ -- Now check the results from plain indexscan
+ SET enable_seqscan = OFF;
+ SET enable_indexscan = ON;
+ SET enable_bitmapscan = OFF;
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+                            QUERY PLAN                           
+ ----------------------------------------------------------------
+  Sort
+    Sort Key: ((home_base[0])[0])
+    ->  Index Only Scan using grect2ind on fast_emp4000
+          Index Cond: (home_base @ '(2000,1000),(200,200)'::box)
+ (4 rows)
+ 
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+        home_base       
+ -----------------------
+  (337,455),(240,359)
+  (1444,403),(1346,344)
+ (2 rows)
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+                          QUERY PLAN                          
+ -------------------------------------------------------------
+  Aggregate
+    ->  Index Only Scan using grect2ind on fast_emp4000
+          Index Cond: (home_base && '(1000,1000),(0,0)'::box)
+ (3 rows)
+ 
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+  count 
+ -------
+      2
+ (1 row)
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+                       QUERY PLAN                       
+ -------------------------------------------------------
+  Aggregate
+    ->  Index Only Scan using grect2ind on fast_emp4000
+          Index Cond: (home_base IS NULL)
+ (3 rows)
+ 
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+  count 
+ -------
+    278
+ (1 row)
+ 
+ -- Try to drop access method: fail because of depending objects
+ DROP ACCESS METHOD gist2;
+ ERROR:  cannot drop access method gist2 because other objects depend on it
+ DETAIL:  operator class box_ops for access method gist2 depends on access method gist2
+ index grect2ind depends on operator class box_ops for access method gist2
+ HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+ -- Drop access method cascade
+ DROP ACCESS METHOD gist2 CASCADE;
+ NOTICE:  drop cascades to 2 other objects
+ DETAIL:  drop cascades to operator class box_ops for access method gist2
+ drop cascades to index grect2ind
+ -- Reset optimizer options
+ RESET enable_seqscan;
+ RESET enable_indexscan;
+ RESET enable_bitmapscan;
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
new file mode 100644
index eb0bc88..2c5be4b
*** a/src/test/regress/expected/sanity_check.out
--- b/src/test/regress/expected/sanity_check.out
*************** e_star|f
*** 44,50 ****
  emp|f
  equipment_r|f
  f_star|f
! fast_emp4000|t
  float4_tbl|f
  float8_tbl|f
  func_index_heap|t
--- 44,50 ----
  emp|f
  equipment_r|f
  f_star|f
! fast_emp4000|f
  float4_tbl|f
  float8_tbl|f
  func_index_heap|t
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
new file mode 100644
index bec0316..8be4b83
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
*************** test: create_index create_view
*** 60,66 ****
  # ----------
  # Another group of parallel tests
  # ----------
! test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes
  
  # ----------
  # sanity_check does a vacuum, affecting the sort order of SELECT *
--- 60,66 ----
  # ----------
  # Another group of parallel tests
  # ----------
! test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes create_am
  
  # ----------
  # sanity_check does a vacuum, affecting the sort order of SELECT *
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
new file mode 100644
index 7e9b319..1de3da8
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
*************** test: drop_if_exists
*** 75,80 ****
--- 75,81 ----
  test: updatable_views
  test: rolenames
  test: roleattributes
+ test: create_am
  test: sanity_check
  test: errors
  test: select
diff --git a/src/test/regress/sql/create_am.sql b/src/test/regress/sql/create_am.sql
new file mode 100644
index ...e2051c5
*** a/src/test/regress/sql/create_am.sql
--- b/src/test/regress/sql/create_am.sql
***************
*** 0 ****
--- 1,73 ----
+ --
+ -- Create access method tests
+ --
+ 
+ -- Make gist2 over gisthandler. In fact, it would be a synonym to gist.
+ CREATE ACCESS METHOD gist2 TYPE INDEX HANDLER gisthandler;
+ 
+ -- Drop old index on fast_emp4000
+ DROP INDEX grect2ind;
+ 
+ -- Try to create gist2 index on fast_emp4000: fail because opclass doesn't exist
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ 
+ -- Make operator class for boxes using gist2
+ CREATE OPERATOR CLASS box_ops DEFAULT
+ 	FOR TYPE box USING gist2 AS
+ 	OPERATOR 1	<<,
+ 	OPERATOR 2	&<,
+ 	OPERATOR 3	&&,
+ 	OPERATOR 4	&>,
+ 	OPERATOR 5	>>,
+ 	OPERATOR 6	~=,
+ 	OPERATOR 7	@>,
+ 	OPERATOR 8	<@,
+ 	OPERATOR 9	&<|,
+ 	OPERATOR 10	<<|,
+ 	OPERATOR 11	|>>,
+ 	OPERATOR 12	|&>,
+ 	OPERATOR 13	~,
+ 	OPERATOR 14	@,
+ 	FUNCTION 1	gist_box_consistent(internal, box, smallint, oid, internal),
+ 	FUNCTION 2	gist_box_union(internal, internal),
+ 	FUNCTION 3	gist_box_compress(internal),
+ 	FUNCTION 4	gist_box_decompress(internal),
+ 	FUNCTION 5	gist_box_penalty(internal, internal, internal),
+ 	FUNCTION 6	gist_box_picksplit(internal, internal),
+ 	FUNCTION 7	gist_box_same(box, box, internal),
+ 	FUNCTION 9	gist_box_fetch(internal);
+ 
+ -- Create gist2 index on fast_emp4000
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ 
+ -- Now check the results from plain indexscan
+ SET enable_seqscan = OFF;
+ SET enable_indexscan = ON;
+ SET enable_bitmapscan = OFF;
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+ 
+ -- Try to drop access method: fail because of depending objects
+ DROP ACCESS METHOD gist2;
+ 
+ -- Drop access method cascade
+ DROP ACCESS METHOD gist2 CASCADE;
+ 
+ -- Reset optimizer options
+ RESET enable_seqscan;
+ RESET enable_indexscan;
+ RESET enable_bitmapscan;
generic-xlog.10.patchapplication/octet-stream; name=generic-xlog.10.patchDownload
diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
new file mode 100644
index c72a1f2..c0e38fd
*** a/src/backend/access/rmgrdesc/Makefile
--- b/src/backend/access/rmgrdesc/Makefile
*************** subdir = src/backend/access/rmgrdesc
*** 8,16 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o gindesc.o gistdesc.o \
! 	   hashdesc.o heapdesc.o mxactdesc.o nbtdesc.o relmapdesc.o \
! 	   replorigindesc.o seqdesc.o smgrdesc.o spgdesc.o \
  	   standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
  
  include $(top_srcdir)/src/backend/common.mk
--- 8,16 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o genericdesc.o \
! 	   gindesc.o gistdesc.o hashdesc.o heapdesc.o mxactdesc.o nbtdesc.o \
! 	   relmapdesc.o replorigindesc.o seqdesc.o smgrdesc.o spgdesc.o \
  	   standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
  
  include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/genericdesc.c b/src/backend/access/rmgrdesc/genericdesc.c
new file mode 100644
index ...3d035c2
*** a/src/backend/access/rmgrdesc/genericdesc.c
--- b/src/backend/access/rmgrdesc/genericdesc.c
***************
*** 0 ****
--- 1,58 ----
+ /*-------------------------------------------------------------------------
+  *
+  * genericdesc.c
+  *	  rmgr descriptor routines for access/transam/generic_xlog.c
+  *
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/backend/access/rmgrdesc/genericdesc.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/generic_xlog.h"
+ #include "lib/stringinfo.h"
+ #include "storage/relfilenode.h"
+ 
+ /*
+  * Description of generic xlog record: write page regions which this record
+  * overrides.
+  */
+ void
+ generic_desc(StringInfo buf, XLogReaderState *record)
+ {
+ 	Pointer		ptr = XLogRecGetData(record),
+ 				end = ptr + XLogRecGetDataLen(record);
+ 
+ 	while (ptr < end)
+ 	{
+ 		OffsetNumber	offset,
+ 						length;
+ 
+ 		memcpy(&offset, ptr, sizeof(offset));
+ 		ptr += sizeof(offset);
+ 		memcpy(&length, ptr, sizeof(length));
+ 		ptr += sizeof(length);
+ 		ptr += length;
+ 
+ 		if (ptr < end)
+ 			appendStringInfo(buf, "offset %u, length %u; ", offset, length);
+ 		else
+ 			appendStringInfo(buf, "offset %u, length %u", offset, length);
+ 	}
+ 
+ 	return;
+ }
+ 
+ /*
+  * Identification of generic xlog record: we don't distinguish any subtypes
+  * inside generic xlog records.
+  */
+ const char *
+ generic_identify(uint8 info)
+ {
+ 	return "Generic";
+ }
diff --git a/src/backend/access/transam/Makefile b/src/backend/access/transam/Makefile
new file mode 100644
index 94455b2..16fbe47
*** a/src/backend/access/transam/Makefile
--- b/src/backend/access/transam/Makefile
*************** subdir = src/backend/access/transam
*** 12,19 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = clog.o commit_ts.o multixact.o parallel.o rmgr.o slru.o subtrans.o \
! 	timeline.o transam.o twophase.o twophase_rmgr.o varsup.o \
  	xact.o xlog.o xlogarchive.o xlogfuncs.o \
  	xloginsert.o xlogreader.o xlogutils.o
  
--- 12,19 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = clog.o commit_ts.o generic_xlog.o multixact.o parallel.o rmgr.o slru.o \
! 	subtrans.o timeline.o transam.o twophase.o twophase_rmgr.o varsup.o \
  	xact.o xlog.o xlogarchive.o xlogfuncs.o \
  	xloginsert.o xlogreader.o xlogutils.o
  
diff --git a/src/backend/access/transam/generic_xlog.c b/src/backend/access/transam/generic_xlog.c
new file mode 100644
index ...e6f26a5
*** a/src/backend/access/transam/generic_xlog.c
--- b/src/backend/access/transam/generic_xlog.c
***************
*** 0 ****
--- 1,508 ----
+ /*-------------------------------------------------------------------------
+  *
+  * generic_xlog.c
+  *	 Implementation of generic xlog records.
+  *
+  *
+  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/backend/access/transam/generic_xlog.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/generic_xlog.h"
+ #include "access/xlogutils.h"
+ #include "miscadmin.h"
+ #include "utils/memutils.h"
+ 
+ /*-------------------------------------------------------------------------
+  * API for construction of generic xlog records
+  *
+  * This API allows user to construct generic xlog records which are
+  * describing difference between pages in general way.  Thus it's useful
+  * for extension which provides custom access methods because they couldn't
+  * register their own WAL redo routines.
+  *
+  * Generic xlog record should be constructed in following steps.
+  * 1) GenericXLogStart(relation) - start construction of generic xlog
+  *	  record for given relation.
+  * 2) GenericXLogRegister(buffer, isNew) - register one or more buffers
+  *	  for generic xlog record.  This function return a copy of page image
+  *	  where modifications should be performed.  The second argument
+  *	  indicates that block is new and full image should be taken.
+  * 3) Do modification of page images obtained in previous step.
+  * 4) GenericXLogFinish() - finish construction of generic xlog record.
+  *
+  * Please, note following points while constructing generic xlog records.
+  * - No direct modifications of page images are allowed! All modifications
+  *	 should be done in copies returned by GenericXLogRegister().  Literally
+  *	 code which makes generic xlog records should never call
+  *	 BufferGetPage() function.
+  * - On any step generic xlog record construction could be canceled by
+  *	 calling GenericXLogAbort().  All changes made in page images copies
+  *	 would be discarded.
+  * - Registrations of buffers (step 2) and modifications of page images
+  *	 (step 3) could be mixed in any sequence.  The only restriction is that
+  *	 you can modify page image only after registration of corresponding
+  *	 buffer.
+  * - After registration buffer also can be unregistered by calling
+  *	 GenericXLogUnregister(buffer).  In this case changes made in particular
+  *	 page image copy will be discarded.
+  * - Generic xlog assumes that pages are using standard layout.  I.e. all
+  *	 information between pd_lower and pd_upper will be discarded.
+  * - Maximum number of buffers simultaneously registered for generic xlog
+  *	 is MAX_GENERIC_XLOG_PAGES.  Error would be thrown if this limit
+  *	 exceeded.
+  * - Since you modify copies of page images, GenericXLogStart() doesn't
+  *	 start a critical section.  Thus, you can do memory allocation, error
+  *	 throwing etc between GenericXLogStart() and GenericXLogFinish().
+  *	 Actual critical section present inside GenericXLogFinish().
+  * - GenericXLogFinish() takes care about marking buffers dirty and setting
+  *	 their LSNs.  You don't need to do this explicitly.
+  * - For unlogged relations, everything work the same expect there is no
+  *	 WAL record produced.  Thus, you typically don't need to do any explicit
+  *	 checks for unlogged relations.
+  * - If registered buffer isn't new, generic xlog record contains delta
+  *	 between old and new page images.  This delta is produced by per byte
+  *	 comparison.  Current delta mechanist is not effective for data shift
+  *	 inside the page.  However, it could be improved in further versions.
+  * - Generic xlog redo function will acquire exclusive locks to buffers
+  *	 in the same order they were registered.  After redo of all changes
+  *	 locks would be released in the same order.  That could makes sense for
+  *	 concurrency.
+  *
+  * Internally delta between pages consists of set of fragments.  Each fragment
+  * represents changes made in given region of page.  Fragment is described
+  * as following.
+  *
+  * - offset of page region (OffsetNumber)
+  * - length of page region (OffsetNumber)
+  * - data - the data to place into described region ('length' number of bytes)
+  *
+  * Unchanged regions of page are uncovered by these fragments.  This is why
+  * delta could be more compact than full page image.  But if unchanged region
+  * of page is less than fragment header (offset and length) then it would
+  * increase size of delta instead of decreasing.  Thus, we break fragment only
+  * for unchanged regions greater than MATCH_THRESHOLD.
+  *
+  * The worst case for delta size is when we didn't find any unchanged region
+  * in the page. Then size of delta would be size of page plus size of fragment
+  * header.
+  */
+ #define FRAGMENT_HEADER_SIZE	(2 * sizeof(OffsetNumber))
+ #define MATCH_THRESHOLD			FRAGMENT_HEADER_SIZE
+ #define MAX_DELTA_SIZE			BLCKSZ + FRAGMENT_HEADER_SIZE
+ 
+ /* Struct of generic xlog data for single page */
+ typedef struct
+ {
+ 	Buffer	buffer;			/* registered buffer */
+ 	char	image[BLCKSZ];	/* copy of page image for modification */
+ 	char	data[MAX_DELTA_SIZE]; /* delta between page images */
+ 	int		dataLen;		/* space consumed in data field */
+ 	bool	fullImage;		/* are we taking full image of this page? */
+ } PageData;
+ 
+ /* Enum of generic xlog (gxlog) status */
+ enum GenericXlogStatus
+ {
+ 	GXLOG_NOT_STARTED,	/* gxlog is not started */
+ 	GXLOG_LOGGED,		/* gxlog is started for logged relation */
+ 	GXLOG_UNLOGGED		/* gxlog is started for unlogged relation */
+ };
+ 
+ static enum GenericXlogStatus	genericXlogStatus = GXLOG_NOT_STARTED;
+ static PageData					pages[MAX_GENERIC_XLOG_PAGES];
+ 
+ 
+ static void writeFragment(PageData *pageData, OffsetNumber offset,
+ 						  OffsetNumber len, Pointer data);
+ static void writeDelta(PageData *pageData);
+ static void applyPageRedo(Page page, Pointer data, Size dataSize);
+ 
+ /*
+  * Write next fragment into delta.
+  */
+ static void
+ writeFragment(PageData *pageData, OffsetNumber offset, OffsetNumber length,
+ 			  Pointer data)
+ {
+ 	Pointer			ptr = pageData->data + pageData->dataLen;
+ 
+ 	/* Check we have enough of space */
+ 	Assert(pageData->dataLen + sizeof(offset) +
+ 		   sizeof(length) + length <= sizeof(pageData->data));
+ 
+ 	/* Write fragment data */
+ 	memcpy(ptr, &offset, sizeof(offset));
+ 	ptr += sizeof(offset);
+ 	memcpy(ptr, &length, sizeof(length));
+ 	ptr += sizeof(length);
+ 	memcpy(ptr, data, length);
+ 	ptr += length;
+ 
+ 	pageData->dataLen = ptr - pageData->data;
+ }
+ 
+ /*
+  * Make delta for given page.
+  */
+ static void
+ writeDelta(PageData *pageData)
+ {
+ 	Page			page = BufferGetPage(pageData->buffer),
+ 					image = (Page) pageData->image;
+ 	int				i,
+ 					fragmentBegin = -1,
+ 					fragmentEnd = -1;
+ 	uint16			pageLower = ((PageHeader) page)->pd_lower,
+ 					pageUpper = ((PageHeader) page)->pd_upper,
+ 					imageLower = ((PageHeader) image)->pd_lower,
+ 					imageUpper = ((PageHeader) image)->pd_upper;
+ 
+ 	for (i = 0; i < BLCKSZ; i++)
+ 	{
+ 		bool	match;
+ 
+ 		/*
+ 		 * Check if bytes in old and new page images matches.  We don't rely
+ 		 * data in unallocated area between pd_lower and pd_upper.  Thus we
+ 		 * assume unallocated area to expand with unmatched bytes.  Bytes
+ 		 * inside unallocated area are assumed to always match.
+ 		 */
+ 		if (i < pageLower)
+ 		{
+ 			if (i < imageLower)
+ 				match = (page[i] == image[i]);
+ 			else
+ 				match = false;
+ 		}
+ 		else if (i >= pageUpper)
+ 		{
+ 			if (i >= imageUpper)
+ 				match = (page[i] == image[i]);
+ 			else
+ 				match = false;
+ 		}
+ 		else
+ 		{
+ 			match = true;
+ 		}
+ 
+ 		if (match)
+ 		{
+ 			if (fragmentBegin >= 0)
+ 			{
+ 				/* Matched byte is potential of fragment. */
+ 				if (fragmentEnd < 0)
+ 					fragmentEnd = i;
+ 
+ 				/*
+ 				 * Write next fragment if sequence of matched bytes is longer
+ 				 * than MATCH_THRESHOLD.
+ 				 */
+ 				if (i - fragmentEnd >= MATCH_THRESHOLD)
+ 				{
+ 					writeFragment(pageData, fragmentBegin,
+ 								  fragmentEnd - fragmentBegin,
+ 								  page + fragmentBegin);
+ 					fragmentBegin = -1;
+ 					fragmentEnd = -1;
+ 				}
+ 			}
+ 		}
+ 		else
+ 		{
+ 			/* On unmatched byte, start new fragment if it's not done yet */
+ 			if (fragmentBegin < 0)
+ 				fragmentBegin = i;
+ 			fragmentEnd = -1;
+ 		}
+ 	}
+ 
+ 	if (fragmentBegin >= 0)
+ 		writeFragment(pageData, fragmentBegin,
+ 					  BLCKSZ - fragmentBegin,
+ 					  page + fragmentBegin);
+ 
+ #ifdef WAL_DEBUG
+ 	/*
+ 	 * If xlog debug is enabled then check produced delta.  Result of delta
+ 	 * application to saved image should be the same as current page state.
+ 	 */
+ 	if (XLOG_DEBUG)
+ 	{
+ 		char	tmp[BLCKSZ];
+ 		memcpy(tmp, image, BLCKSZ);
+ 		applyPageRedo(tmp, pageData->data, pageData->dataLen);
+ 		elog(ERROR, "result of generic xlog apply doesn't match");
+ 	}
+ #endif
+ }
+ 
+ /*
+  * Start new generic xlog record.
+  */
+ void
+ GenericXLogStart(Relation relation)
+ {
+ 	int i;
+ 
+ 	if (genericXlogStatus != GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogStart: generic xlog is already started")));
+ 
+ 	genericXlogStatus = RelationNeedsWAL(relation) ? GXLOG_LOGGED : GXLOG_UNLOGGED;
+ 
+ 	for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 	{
+ 		pages[i].buffer = InvalidBuffer;
+ 	}
+ }
+ 
+ /*
+  * Register new buffer for generic xlog record.
+  */
+ Page
+ GenericXLogRegister(Buffer buffer, bool isNew)
+ {
+ 	int block_id;
+ 
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogRegister: generic xlog isn't started")));
+ 
+ 	/* Place new buffer to unused slot in array */
+ 	for (block_id = 0; block_id < MAX_GENERIC_XLOG_PAGES; block_id++)
+ 	{
+ 		if (BufferIsInvalid(pages[block_id].buffer))
+ 		{
+ 			pages[block_id].buffer = buffer;
+ 			memcpy(pages[block_id].image, BufferGetPage(buffer), BLCKSZ);
+ 			pages[block_id].dataLen = 0;
+ 			pages[block_id].fullImage = isNew;
+ 			return (Page)pages[block_id].image;
+ 		}
+ 		else if (pages[block_id].buffer == buffer)
+ 		{
+ 			/* 
+ 			 * Buffer already registered.  Just return image which is already
+ 			 * prepared.
+ 			 */
+ 			return (Page)pages[block_id].image;
+ 		}
+ 	}
+ 
+ 	ereport(ERROR,
+ 			(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ 			 errmsg("GenericXLogRegister: maximum number of %d buffers is exceeded",
+ 					MAX_GENERIC_XLOG_PAGES)));
+ 
+ 	/* keep compiler quiet */
+ 	return NULL;
+ }
+ 
+ /*
+  * Unregister particular buffer for generic xlog record.
+  */
+ void
+ GenericXLogUnregister(Buffer buffer)
+ {
+ 	int block_id;
+ 
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogUnregister: generic xlog isn't started")));
+ 
+ 	/* Find block in array to unregister */
+ 	for (block_id = 0; block_id < MAX_GENERIC_XLOG_PAGES; block_id++)
+ 	{
+ 		if (pages[block_id].buffer == buffer)
+ 		{
+ 			/*
+ 			 * Preserve order of pages in array because it could matter for
+ 			 * concurrency.
+ 			 */
+ 			memmove(&pages[block_id], &pages[block_id + 1],
+ 					(MAX_GENERIC_XLOG_PAGES - block_id - 1) * sizeof(PageData));
+ 			pages[MAX_GENERIC_XLOG_PAGES - 1].buffer = InvalidBuffer;
+ 			return;
+ 		}
+ 	}
+ 
+ 	ereport(ERROR,
+ 			(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 			 errmsg("GenericXLogUnregister: registered buffer not found")));
+ }
+ 
+ /*
+  * Put all changes in registered buffers to generic xlog record.
+  */
+ XLogRecPtr
+ GenericXLogFinish(void)
+ {
+ 	XLogRecPtr lsn = InvalidXLogRecPtr;
+ 	int i;
+ 
+ 	if (genericXlogStatus == GXLOG_LOGGED)
+ 	{
+ 		/* Logged relation: make xlog record in critical section. */
+ 		START_CRIT_SECTION();
+ 		XLogBeginInsert();
+ 
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			char	tmp[BLCKSZ];
+ 
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 
+ 			/* Swap current and saved page image. */
+ 			memcpy(tmp, pages[i].image, BLCKSZ);
+ 			memcpy(pages[i].image, BufferGetPage(pages[i].buffer), BLCKSZ);
+ 			memcpy(BufferGetPage(pages[i].buffer), tmp, BLCKSZ);
+ 
+ 			if (pages[i].fullImage)
+ 			{
+ 				/* Full page image doesn't require anything special from us */
+ 				XLogRegisterBuffer(i, pages[i].buffer, REGBUF_FORCE_IMAGE);
+ 			}
+ 			else
+ 			{
+ 				/*
+ 				 * In normal node calculate delta and write use it as data
+ 				 * associated with this page.
+ 				 */
+ 				XLogRegisterBuffer(i, pages[i].buffer, REGBUF_STANDARD);
+ 				writeDelta(&pages[i]);
+ 				XLogRegisterBufData(i, pages[i].data, pages[i].dataLen);
+ 			}
+ 		}
+ 
+ 		/* Insert xlog record */
+ 		lsn = XLogInsert(RM_GENERIC_ID, 0);
+ 
+ 		/* Set LSN and make buffers dirty */
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 			PageSetLSN(BufferGetPage(pages[i].buffer), lsn);
+ 			MarkBufferDirty(pages[i].buffer);
+ 		}
+ 		END_CRIT_SECTION();
+ 	}
+ 	else if (genericXlogStatus == GXLOG_UNLOGGED)
+ 	{
+ 		/* Unlogged relation: skip xlog-related stuff */
+ 		START_CRIT_SECTION();
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 			memcpy(BufferGetPage(pages[i].buffer), pages[i].image, BLCKSZ);
+ 			MarkBufferDirty(pages[i].buffer);
+ 		}
+ 		END_CRIT_SECTION();
+ 	}
+ 	else
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogFinish: generic xlog isn't started")));
+ 	}
+ 
+ 	genericXlogStatus = GXLOG_NOT_STARTED;
+ 
+ 	return lsn;
+ }
+ 
+ /*
+  * Abort generic xlog record.
+  */
+ void
+ GenericXLogAbort(void)
+ {
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogAbort: generic xlog isn't started")));
+ 
+ 	genericXlogStatus = GXLOG_NOT_STARTED;
+ }
+ 
+ /*
+  * Apply delta to given page image.
+  */
+ static void
+ applyPageRedo(Page page, Pointer data, Size dataSize)
+ {
+ 	Pointer ptr = data, end = data + dataSize;
+ 
+ 	while (ptr < end)
+ 	{
+ 		OffsetNumber	offset,
+ 						length;
+ 
+ 		memcpy(&offset, ptr, sizeof(offset));
+ 		ptr += sizeof(offset);
+ 		memcpy(&length, ptr, sizeof(length));
+ 		ptr += sizeof(length);
+ 
+ 		memcpy(page + offset, ptr, length);
+ 
+ 		ptr += length;
+ 	}
+ }
+ 
+ /*
+  * Redo function for generic xlog record.
+  */
+ void
+ generic_redo(XLogReaderState *record)
+ {
+ 	uint8		block_id;
+ 	Buffer		buffers[MAX_GENERIC_XLOG_PAGES] = {InvalidBuffer};
+ 	XLogRecPtr	lsn = record->EndRecPtr;
+ 
+ 	Assert(record->max_block_id < MAX_GENERIC_XLOG_PAGES);
+ 
+ 	/* Interate over blocks */
+ 	for (block_id = 0; block_id <= record->max_block_id; block_id++)
+ 	{
+ 		XLogRedoAction action;
+ 
+ 		if (!XLogRecHasBlockRef(record, block_id))
+ 			continue;
+ 
+ 		action = XLogReadBufferForRedo(record, block_id, &buffers[block_id]);
+ 
+ 		/* Apply redo to given block if needed */
+ 		if (action == BLK_NEEDS_REDO)
+ 		{
+ 			Pointer	blockData;
+ 			Size	blockDataSize;
+ 			Page	page;
+ 
+ 			page = BufferGetPage(buffers[block_id]);
+ 			blockData = XLogRecGetBlockData(record, block_id, &blockDataSize);
+ 			applyPageRedo(page, blockData, blockDataSize);
+ 
+ 			PageSetLSN(page, lsn);
+ 			MarkBufferDirty(buffers[block_id]);
+ 		}
+ 	}
+ 
+ 	/* Changes are done: unlock and release all buffers */
+ 	for (block_id = 0; block_id <= record->max_block_id; block_id++)
+ 	{
+ 		if (BufferIsValid(buffers[block_id]))
+ 			UnlockReleaseBuffer(buffers[block_id]);
+ 	}
+ }
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
new file mode 100644
index 7c4d773..7b38c16
*** a/src/backend/access/transam/rmgr.c
--- b/src/backend/access/transam/rmgr.c
***************
*** 11,16 ****
--- 11,17 ----
  #include "access/commit_ts.h"
  #include "access/gin.h"
  #include "access/gist_private.h"
+ #include "access/generic_xlog.h"
  #include "access/hash.h"
  #include "access/heapam_xlog.h"
  #include "access/brin_xlog.h"
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
new file mode 100644
index 13af485..262deb2
*** a/src/backend/replication/logical/decode.c
--- b/src/backend/replication/logical/decode.c
*************** LogicalDecodingProcessRecord(LogicalDeco
*** 143,148 ****
--- 143,149 ----
  		case RM_BRIN_ID:
  		case RM_COMMIT_TS_ID:
  		case RM_REPLORIGIN_ID:
+ 		case RM_GENERIC_ID:
  			/* just deal with xid, and done */
  			ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(record),
  									buf.origptr);
diff --git a/src/bin/pg_xlogdump/.gitignore b/src/bin/pg_xlogdump/.gitignore
new file mode 100644
index eebaf30..33a1acf
*** a/src/bin/pg_xlogdump/.gitignore
--- b/src/bin/pg_xlogdump/.gitignore
***************
*** 4,9 ****
--- 4,10 ----
  /clogdesc.c
  /committsdesc.c
  /dbasedesc.c
+ /genericdesc.c
  /gindesc.c
  /gistdesc.c
  /hashdesc.c
diff --git a/src/bin/pg_xlogdump/rmgrdesc.c b/src/bin/pg_xlogdump/rmgrdesc.c
new file mode 100644
index f9cd395..cff7e59
*** a/src/bin/pg_xlogdump/rmgrdesc.c
--- b/src/bin/pg_xlogdump/rmgrdesc.c
***************
*** 11,16 ****
--- 11,17 ----
  #include "access/brin_xlog.h"
  #include "access/clog.h"
  #include "access/commit_ts.h"
+ #include "access/generic_xlog.h"
  #include "access/gin.h"
  #include "access/gist_private.h"
  #include "access/hash.h"
diff --git a/src/include/access/generic_xlog.h b/src/include/access/generic_xlog.h
new file mode 100644
index ...49249e0
*** a/src/include/access/generic_xlog.h
--- b/src/include/access/generic_xlog.h
***************
*** 0 ****
--- 1,36 ----
+ /*-------------------------------------------------------------------------
+  *
+  * generic_xlog.h
+  *	  Generic xlog API definition.
+  *
+  *
+  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/access/generic_xlog.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef GENERIC_XLOG_H
+ #define GENERIC_XLOG_H
+ 
+ #include "access/xlog.h"
+ #include "access/xlog_internal.h"
+ #include "storage/bufpage.h"
+ #include "utils/rel.h"
+ 
+ #define MAX_GENERIC_XLOG_PAGES	  3
+ 
+ /* API for construction of generic xlog records */
+ extern void GenericXLogStart(Relation relation);
+ extern Page GenericXLogRegister(Buffer buffer, bool isNew);
+ extern void GenericXLogUnregister(Buffer buffer);
+ extern XLogRecPtr GenericXLogFinish(void);
+ extern void GenericXLogAbort(void);
+ 
+ /* functions defined for rmgr */
+ extern void generic_redo(XLogReaderState *record);
+ extern const char *generic_identify(uint8 info);
+ extern void generic_desc(StringInfo buf, XLogReaderState *record);
+ 
+ #endif   /* GENERIC_XLOG_H */
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
new file mode 100644
index fab912d..3cfe6f7
*** a/src/include/access/rmgrlist.h
--- b/src/include/access/rmgrlist.h
*************** PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo
*** 45,47 ****
--- 45,48 ----
  PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL)
  PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL)
  PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL)
+ PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL)
bloom-contrib.10.patchapplication/octet-stream; name=bloom-contrib.10.patchDownload
diff --git a/contrib/Makefile b/contrib/Makefile
new file mode 100644
index d12dd63..25263c0
*** a/contrib/Makefile
--- b/contrib/Makefile
*************** SUBDIRS = \
*** 8,13 ****
--- 8,14 ----
  		adminpack	\
  		auth_delay	\
  		auto_explain	\
+ 		bloom		\
  		btree_gin	\
  		btree_gist	\
  		chkpass		\
diff --git a/contrib/bloom/.gitignore b/contrib/bloom/.gitignore
new file mode 100644
index ...5dcb3ff
*** a/contrib/bloom/.gitignore
--- b/contrib/bloom/.gitignore
***************
*** 0 ****
--- 1,4 ----
+ # Generated subdirectories
+ /log/
+ /results/
+ /tmp_check/
diff --git a/contrib/bloom/Makefile b/contrib/bloom/Makefile
new file mode 100644
index ...13bd397
*** a/contrib/bloom/Makefile
--- b/contrib/bloom/Makefile
***************
*** 0 ****
--- 1,24 ----
+ # contrib/bloom/Makefile
+ 
+ MODULE_big = bloom
+ OBJS = blcost.o blinsert.o blscan.o blutils.o blvacuum.o blvalidate.o $(WIN32RES)
+ 
+ EXTENSION = bloom
+ DATA = bloom--1.0.sql
+ PGFILEDESC = "bloom access method - signature file based index"
+ 
+ REGRESS = bloom
+ 
+ ifdef USE_PGXS
+ PG_CONFIG = pg_config
+ PGXS := $(shell $(PG_CONFIG) --pgxs)
+ include $(PGXS)
+ else
+ subdir = contrib/bloom
+ top_builddir = ../..
+ include $(top_builddir)/src/Makefile.global
+ include $(top_srcdir)/contrib/contrib-global.mk
+ endif
+ 
+ wal-check: temp-install
+ 	$(prove_check)
diff --git a/contrib/bloom/blcost.c b/contrib/bloom/blcost.c
new file mode 100644
index ...d918dce
*** a/contrib/bloom/blcost.c
--- b/contrib/bloom/blcost.c
***************
*** 0 ****
--- 1,45 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blcost.c
+  *		Cost estimate function for bloom indexes.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blcost.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "fmgr.h"
+ #include "optimizer/cost.h"
+ #include "utils/selfuncs.h"
+ 
+ #include "bloom.h"
+ 
+ void
+ blcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
+ 			   Cost *indexStartupCost, Cost *indexTotalCost,
+ 			   Selectivity *indexSelectivity, double *indexCorrelation)
+ {
+ 	IndexOptInfo   *index = path->indexinfo;
+ 	List		   *qinfos;
+ 	GenericCosts	costs;
+ 
+ 	/* Do preliminary analysis of indexquals */
+ 	qinfos = deconstruct_indexquals(path);
+ 
+ 	MemSet(&costs, 0, sizeof(costs));
+ 
+ 	/* We have to visit all index tuples anyway */
+ 	costs.numIndexTuples = index->tuples;
+ 
+ 	/* Use generic estimate */
+ 	genericcostestimate(root, path, loop_count, qinfos, &costs);
+ 
+ 	*indexStartupCost = costs.indexStartupCost;
+ 	*indexTotalCost = costs.indexTotalCost;
+ 	*indexSelectivity = costs.indexSelectivity;
+ 	*indexCorrelation = costs.indexCorrelation;
+ }
diff --git a/contrib/bloom/blinsert.c b/contrib/bloom/blinsert.c
new file mode 100644
index ...15bff40
*** a/contrib/bloom/blinsert.c
--- b/contrib/bloom/blinsert.c
***************
*** 0 ****
--- 1,293 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blinsert.c
+  *		Bloom index build and insert functions.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blinsert.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "access/generic_xlog.h"
+ #include "catalog/index.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ 
+ #include "bloom.h"
+ 
+ PG_MODULE_MAGIC;
+ 
+ typedef struct
+ {
+ 	BloomState		blstate;
+ 	MemoryContext	tmpCtx;
+ 	char			data[BLCKSZ];
+ 	int64			count;
+ } BloomBuildState;
+ 
+ /*
+  * Flush page cached in BloomBuildState.
+  */
+ static void
+ flushCachedPage(Relation index, BloomBuildState *buildstate)
+ {
+ 	Page	page;
+ 	Buffer	buffer = BloomNewBuffer(index);
+ 
+ 	GenericXLogStart(index);
+ 	page = GenericXLogRegister(buffer, true);
+ 	memcpy(page, buildstate->data, BLCKSZ);
+ 	GenericXLogFinish();
+ 	UnlockReleaseBuffer(buffer);
+ }
+ 
+ /*
+  * (Re)initialize cached page in BloomBuildState.
+  */
+ static void
+ initCachedPage(BloomBuildState *buildstate)
+ {
+ 	memset(buildstate->data, 0, BLCKSZ);
+ 	BloomInitPage(buildstate->data, 0);
+ 	buildstate->count = 0;
+ }
+ 
+ /*
+  * Per-tuple callback from IndexBuildHeapScan.
+  */
+ static void
+ bloomBuildCallback(Relation index, HeapTuple htup, Datum *values,
+ 					bool *isnull, bool tupleIsAlive, void *state)
+ {
+ 	BloomBuildState	*buildstate = (BloomBuildState *)state;
+ 	MemoryContext	 oldCtx;
+ 	BloomTuple		*itup;
+ 
+ 	oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
+ 
+ 	itup = BloomFormTuple(&buildstate->blstate, &htup->t_self, values, isnull);
+ 
+ 	/* Try to add next item to cached page */
+ 	if (BloomPageAddItem(&buildstate->blstate, buildstate->data, itup) == false)
+ 	{
+ 		/* Cached page is full, flush it out and make a new one */
+ 		flushCachedPage(index, buildstate);
+ 
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		initCachedPage(buildstate);
+ 
+ 		if (BloomPageAddItem(&buildstate->blstate, buildstate->data, itup) == false)
+ 		{
+ 			/* We shouldn't be here since we're inserting to the empty page */
+ 			elog(ERROR, "can not add new tuple");
+ 		}
+ 	}
+ 	else
+ 	{
+ 		buildstate->count++;
+ 	}
+ 
+ 	MemoryContextSwitchTo(oldCtx);
+ 	MemoryContextReset(buildstate->tmpCtx);
+ }
+ 
+ IndexBuildResult *
+ blbuild(Relation heap, Relation index, IndexInfo *indexInfo)
+ {
+ 	IndexBuildResult   *result;
+ 	double				reltuples;
+ 	BloomBuildState		buildstate;
+ 
+ 	if (RelationGetNumberOfBlocks(index) != 0)
+ 		elog(ERROR, "index \"%s\" already contains data",
+ 			RelationGetRelationName(index));
+ 
+ 	/* Initialize the meta page */
+ 	BloomInitMetapage(index);
+ 
+ 	initBloomState(&buildstate.blstate, index);
+ 
+ 	buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
+ 												"Bloom build temporary context",
+ 												ALLOCSET_DEFAULT_MINSIZE,
+ 												ALLOCSET_DEFAULT_INITSIZE,
+ 												ALLOCSET_DEFAULT_MAXSIZE);
+ 
+ 	initCachedPage(&buildstate);
+ 
+ 	/* Do the heap scan */
+ 	reltuples = IndexBuildHeapScan(heap, index, indexInfo, true,
+ 									bloomBuildCallback, (void *) &buildstate);
+ 
+ 	if (buildstate.count > 0)
+ 		flushCachedPage(index, &buildstate);
+ 
+ 	MemoryContextDelete(buildstate.tmpCtx);
+ 
+ 	result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
+ 	result->heap_tuples = result->index_tuples = reltuples;
+ 
+ 	return result;
+ }
+ 
+ void
+ blbuildempty(Relation index)
+ {
+ 	if (RelationGetNumberOfBlocks(index) != 0)
+ 		elog(ERROR, "index \"%s\" already contains data",
+ 			RelationGetRelationName(index));
+ 
+ 	/* Initialize the meta page */
+ 	BloomInitMetapage(index);
+ }
+ 
+ bool
+ blinsert(Relation index, Datum *values, bool *isnull,
+ 		ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique)
+ {
+ 	BloomState			blstate;
+ 	BloomTuple		   *itup;
+ 	MemoryContext		oldCtx;
+ 	MemoryContext		insertCtx;
+ 	BloomMetaPageData  *metaData;
+ 	Buffer				buffer,
+ 						metaBuffer;
+ 	Page				page,
+ 						metaPage;
+ 	BlockNumber			blkno = InvalidBlockNumber;
+ 	OffsetNumber		nStart;
+ 
+ 	insertCtx = AllocSetContextCreate(CurrentMemoryContext,
+ 										"Bloom insert temporary context",
+ 										ALLOCSET_DEFAULT_MINSIZE,
+ 										ALLOCSET_DEFAULT_INITSIZE,
+ 										ALLOCSET_DEFAULT_MAXSIZE);
+ 
+ 	oldCtx = MemoryContextSwitchTo(insertCtx);
+ 
+ 	initBloomState(&blstate, index);
+ 	itup = BloomFormTuple(&blstate, ht_ctid, values, isnull);
+ 
+ 	/*
+ 	 * At first, try to insert new tuple to the first page in
+ 	 * notFullPage array.  If success we don't need to modify the
+ 	 * meta page.
+ 	 */
+ 	metaBuffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 	LockBuffer(metaBuffer, BUFFER_LOCK_SHARE);
+ 	metaData = BloomPageGetMeta(BufferGetPage(metaBuffer));
+ 
+ 	if (metaData->nEnd > metaData->nStart)
+ 	{
+ 		Page	page;
+ 
+ 		blkno = metaData->notFullPage[metaData->nStart];
+ 
+ 		Assert(blkno != InvalidBlockNumber);
+ 		LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		GenericXLogStart(index);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		if (BloomPageAddItem(&blstate, page, itup))
+ 		{
+ 			GenericXLogFinish();
+ 			UnlockReleaseBuffer(buffer);
+ 			ReleaseBuffer(metaBuffer);
+ 			goto away;
+ 		}
+ 		else
+ 		{
+ 			GenericXLogAbort();
+ 			UnlockReleaseBuffer(buffer);
+ 		}
+ 	}
+ 	else
+ 	{
+ 		/* First page in notFullPage isn't suitable */
+ 		LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
+ 	}
+ 
+ 	/*
+ 	 * Try other pages in notFullPage array.  We will have to change nStart
+ 	 * in metapage.  Thus, grab exclusive lock on metapage.
+ 	 */
+ 	LockBuffer(metaBuffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 	GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(metaBuffer, false);
+ 	metaData = BloomPageGetMeta(metaPage);
+ 
+ 	/*
+ 	 * Iterate over notFullPage array.  Skip page we already tried first.
+ 	 */
+ 	nStart = metaData->nStart;
+ 	if (metaData->nEnd > nStart &&
+ 		blkno == metaData->notFullPage[nStart] )
+ 		nStart++;
+ 
+ 	while (metaData->nEnd > nStart)
+ 	{
+ 		blkno = metaData->notFullPage[nStart];
+ 		Assert(blkno != InvalidBlockNumber);
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		if (BloomPageAddItem(&blstate, page, itup))
+ 		{
+ 			metaData->nStart = nStart;
+ 			GenericXLogFinish();
+ 			UnlockReleaseBuffer(buffer);
+ 			UnlockReleaseBuffer(metaBuffer);
+ 			goto away;
+ 		}
+ 		else
+ 		{
+ 			GenericXLogUnregister(buffer);
+ 			UnlockReleaseBuffer(buffer);
+ 		}
+ 		nStart++;
+ 	}
+ 
+ 	GenericXLogAbort();
+ 
+ 	/*
+ 	 * Didn't find place to insert in notFullPage array.  Allocate new page.
+ 	 */
+ 	buffer = BloomNewBuffer(index);
+ 
+ 	GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(metaBuffer, false);
+ 	metaData = BloomPageGetMeta(metaPage);
+ 	page = GenericXLogRegister(buffer, true);
+ 	BloomInitPage(page, 0);
+ 	BloomPageAddItem(&blstate, page, itup);
+ 
+ 	metaData->nStart = 0;
+ 	metaData->nEnd = 1;
+ 	metaData->notFullPage[ 0 ] = BufferGetBlockNumber(buffer);
+ 
+ 	GenericXLogFinish();
+ 
+ 	UnlockReleaseBuffer(buffer);
+ 	UnlockReleaseBuffer(metaBuffer);
+ 
+ away:
+ 	MemoryContextSwitchTo(oldCtx);
+ 	MemoryContextDelete(insertCtx);
+ 
+ 	return false;
+ }
diff --git a/contrib/bloom/bloom--1.0.sql b/contrib/bloom/bloom--1.0.sql
new file mode 100644
index ...7fa7513
*** a/contrib/bloom/bloom--1.0.sql
--- b/contrib/bloom/bloom--1.0.sql
***************
*** 0 ****
--- 1,19 ----
+ CREATE OR REPLACE FUNCTION blhandler(internal)
+ RETURNS index_am_handler
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C;
+ 
+ -- Access method
+ CREATE ACCESS METHOD bloom TYPE INDEX HANDLER blhandler;
+ 
+ -- Opclasses
+ 
+ CREATE OPERATOR CLASS int4_ops
+ DEFAULT FOR TYPE int4 USING bloom AS
+ 	OPERATOR	1	=(int4, int4),
+ 	FUNCTION	1	hashint4(int4);
+ 
+ CREATE OPERATOR CLASS text_ops
+ DEFAULT FOR TYPE text USING bloom AS
+ 	OPERATOR	1	=(text, text),
+ 	FUNCTION	1	hashtext(text);
diff --git a/contrib/bloom/bloom.control b/contrib/bloom/bloom.control
new file mode 100644
index ...4d4124b
*** a/contrib/bloom/bloom.control
--- b/contrib/bloom/bloom.control
***************
*** 0 ****
--- 1,5 ----
+ # bloom extension
+ comment = 'bloom access method - signature file based index'
+ default_version = '1.0'
+ module_pathname = '$libdir/bloom'
+ relocatable = true
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
new file mode 100644
index ...6f20ee4
*** a/contrib/bloom/bloom.h
--- b/contrib/bloom/bloom.h
***************
*** 0 ****
--- 1,176 ----
+ /*-------------------------------------------------------------------------
+  *
+  * bloom.h
+  *	  Header for bloom index.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/bloom.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef _BLOOM_H_
+ #define _BLOOM_H_
+ 
+ #include "access/amapi.h"
+ #include "access/generic_xlog.h"
+ #include "access/itup.h"
+ #include "access/xlog.h"
+ #include "nodes/relation.h"
+ #include "fmgr.h"
+ 
+ /* Support procedures numbers */
+ #define	BLOOM_HASH_PROC			1
+ #define	BLOOM_NPROC				1
+ 
+ /* Scan strategies */
+ #define	BLOOM_EQUAL_STRATEGY	1
+ #define	BLOOM_NSTRATEGIES		1
+ 
+ /* Opaque for bloom pages */
+ typedef struct BloomPageOpaqueData
+ {
+ 	OffsetNumber	maxoff;
+ 	uint16			flags;
+ } BloomPageOpaqueData;
+ 
+ typedef BloomPageOpaqueData *BloomPageOpaque;
+ 
+ /* Bloom page flags */
+ #define BLOOM_META		(1<<0)
+ #define BLOOM_DELETED	(2<<0)
+ 
+ #define BloomPageGetOpaque(page) ((BloomPageOpaque) PageGetSpecialPointer(page))
+ #define BloomPageGetMaxOffset(page) (BloomPageGetOpaque(page)->maxoff)
+ #define BloomPageIsMeta(page) (BloomPageGetOpaque(page)->flags & BLOOM_META)
+ #define BloomPageIsDeleted(page) (BloomPageGetOpaque(page)->flags & BLOOM_DELETED)
+ #define BloomPageSetDeleted(page) (BloomPageGetOpaque(page)->flags |= BLOOM_DELETED)
+ #define BloomPageSetNonDeleted(page) (BloomPageGetOpaque(page)->flags &= ~BLOOM_DELETED)
+ #define BloomPageGetData(page)		((BloomTuple *)PageGetContents(page))
+ #define BloomPageGetTuple(state, page, offset) \
+ 	((BloomTuple *)(PageGetContents(page) \
+ 		+ (state)->sizeOfBloomTuple * ((offset) - 1)))
+ #define BloomPageGetNextTuple(state, tuple) \
+ 	((BloomTuple *)((Pointer)(tuple) + (state)->sizeOfBloomTuple))
+ 
+ /* Preserved page numbers */
+ #define BLOOM_METAPAGE_BLKNO	(0)
+ #define BLOOM_HEAD_BLKNO		(1) /* first data page */
+ 
+ /* Bloom index options */
+ typedef struct BloomOptions
+ {
+ 	int32	vl_len_;				 /* varlena header (do not touch directly!) */
+ 	int		bloomLength;			 /* length of signature in uint16 */
+ 	int		bitSize[INDEX_MAX_KEYS]; /* signature bits per index key */
+ } BloomOptions;
+ 
+ /*
+  * FreeBlockNumberArray - array of block numbers sized so that metadata fill
+  * all space in metapage.
+  */
+ typedef BlockNumber FreeBlockNumberArray[
+ 	MAXALIGN_DOWN(
+ 		BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(BloomPageOpaqueData))
+ 			   - MAXALIGN(sizeof(uint16) * 2 + sizeof(uint32) + sizeof(BloomOptions))
+ 	) / sizeof(BlockNumber) 
+ ];
+ 
+ /* Metadata of bloom index */
+ typedef struct BloomMetaPageData
+ {
+ 	uint32					magickNumber;
+ 	uint16					nStart;
+ 	uint16					nEnd;
+ 	BloomOptions			opts;
+ 	FreeBlockNumberArray	notFullPage;
+ } BloomMetaPageData;
+ 
+ /* Magic number to distinguish bloom pages among anothers */
+ #define BLOOM_MAGICK_NUMBER	(0xDBAC0DED)
+ 
+ /* Number of blocks numbers fit in BloomMetaPageData */
+ #define BloomMetaBlockN		(sizeof(FreeBlockNumberArray) / sizeof(BlockNumber))
+ 
+ #define BloomPageGetMeta(page)	((BloomMetaPageData *) PageGetContents(page))
+ 
+ typedef struct BloomState 
+ {
+ 	FmgrInfo			hashFn[INDEX_MAX_KEYS];
+ 	BloomOptions	   *opts; /* stored in rd_amcache and defined at creation time */
+ 	int32				nColumns;
+ 	/* 
+ 	 * sizeOfBloomTuple is index's specific, and it depends on
+ 	 * reloptions, so precompute it
+ 	 */
+ 	int32				sizeOfBloomTuple; 
+ } BloomState;
+ 
+ #define BloomPageGetFreeSpace(state, page) \
+ 	(BLCKSZ - MAXALIGN(SizeOfPageHeaderData) \
+ 		- BloomPageGetMaxOffset(page) * (state)->sizeOfBloomTuple \
+ 		- MAXALIGN(sizeof(BloomPageOpaqueData)))
+ 
+ /*
+  * Tuples are very different from all other relations
+  */
+ typedef uint16	SignType;
+ 
+ typedef struct BloomTuple
+ {
+ 	ItemPointerData		heapPtr;
+ 	SignType			sign[1];
+ } BloomTuple;
+ 
+ #define BLOOMTUPLEHDRSZ	offsetof(BloomTuple, sign)
+ 
+ /* Opaque data structure for bloom index scan */
+ typedef struct BloomScanOpaqueData
+ {
+ 	SignType   *sign;	/* Scan signature */
+ 	BloomState	state;
+ } BloomScanOpaqueData;
+ 
+ typedef BloomScanOpaqueData *BloomScanOpaque;
+ 
+ /* blutils.c */
+ extern void _PG_init(void);
+ extern Datum blhandler(PG_FUNCTION_ARGS);
+ extern void initBloomState(BloomState *state, Relation index);
+ extern void BloomInitMetapage(Relation index);
+ extern void BloomInitPage(Page page, uint16 flags);
+ extern Buffer BloomNewBuffer(Relation index);
+ extern void signValue(BloomState *state, SignType *sign, Datum value, int attno);
+ extern BloomTuple* BloomFormTuple(BloomState *state, ItemPointer iptr, Datum *values, bool *isnull);
+ extern bool BloomPageAddItem(BloomState *state, Page page, BloomTuple *tuple);
+ 
+ /* blvalidate.c */
+ extern bool blvalidate(Oid opclassoid);
+ 
+ /* interface functions */
+ extern bool blinsert(Relation index, Datum *values, bool *isnull,
+ 					 ItemPointer ht_ctid, Relation heapRel,
+ 					 IndexUniqueCheck checkUnique);
+ extern IndexScanDesc blbeginscan(Relation r, int nkeys, int norderbys);
+ extern int64 blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
+ extern void blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ 					 ScanKey orderbys, int norderbys);
+ extern void blendscan(IndexScanDesc scan);
+ extern void blmarkpos(IndexScanDesc scan);
+ extern void blrestrpos(IndexScanDesc scan);
+ extern IndexBuildResult *blbuild(Relation heap, Relation index,
+ 								   struct IndexInfo *indexInfo);
+ extern void blbuildempty(Relation index);
+ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
+ 	IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback,
+ 	void *callback_state);
+ extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
+ 	IndexBulkDeleteResult *stats);
+ extern bytea *bloptions(Datum reloptions, bool validate);
+ extern void blcostestimate(PlannerInfo *root, IndexPath *path,
+ 						   double loop_count, Cost *indexStartupCost,
+ 						   Cost *indexTotalCost, Selectivity *indexSelectivity,
+ 						   double *indexCorrelation);
+ 
+ #endif
diff --git a/contrib/bloom/blscan.c b/contrib/bloom/blscan.c
new file mode 100644
index ...467a1b1
*** a/contrib/bloom/blscan.c
--- b/contrib/bloom/blscan.c
***************
*** 0 ****
--- 1,174 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blscan.c
+  *		Bloom index scan functions.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blscan.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/relscan.h"
+ #include "pgstat.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/lmgr.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ 
+ #include "bloom.h"
+ 
+ IndexScanDesc
+ blbeginscan(Relation r, int nkeys, int norderbys)
+ {
+ 	IndexScanDesc scan;
+ 
+ 	scan = RelationGetIndexScan(r, nkeys, norderbys);
+ 
+ 	return scan;
+ }
+ 
+ void
+ blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ 							 ScanKey orderbys, int norderbys)
+ {
+ 	BloomScanOpaque so;
+ 
+ 	so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so == NULL)
+ 	{
+ 		/* if called from blbeginscan */
+ 		so = (BloomScanOpaque) palloc(sizeof(BloomScanOpaqueData));
+ 		initBloomState(&so->state, scan->indexRelation);
+ 		scan->opaque = so;
+ 
+ 	}
+ 	else
+ 	{
+ 		if (so->sign)
+ 			pfree(so->sign);
+ 	}
+ 	so->sign = NULL;
+ 
+ 	if (scankey && scan->numberOfKeys > 0)
+ 	{
+ 		memmove(scan->keyData, scankey,
+ 				scan->numberOfKeys * sizeof(ScanKeyData));
+ 	}
+ }
+ 
+ void
+ blendscan(IndexScanDesc scan)
+ {
+ 	BloomScanOpaque so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so->sign)
+ 		pfree(so->sign);
+ 	so->sign = NULL;
+ }
+ 
+ void
+ blmarkpos(IndexScanDesc scan)
+ {
+ 	elog(ERROR, "Bloom does not support mark/restore");
+ }
+ 
+ void
+ blrestrpos(IndexScanDesc scan)
+ {
+ 	elog(ERROR, "Bloom does not support mark/restore");
+ }
+ 
+ int64
+ blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
+ {
+ 	int64					ntids = 0;
+ 	BlockNumber				blkno = BLOOM_HEAD_BLKNO,
+ 							npages;
+ 	int						i;
+ 	BufferAccessStrategy	bas;
+ 	BloomScanOpaque 		so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so->sign == NULL && scan->numberOfKeys > 0)
+ 	{
+ 		/* New search: have to calculate search signature */
+ 		ScanKey skey = scan->keyData;
+ 
+ 		so->sign = palloc0(sizeof(SignType) * so->state.opts->bloomLength); 
+ 		
+ 		for(i = 0; i < scan->numberOfKeys; i++)
+ 		{
+ 			/*
+ 			 * Assume bloom-indexable operators to be strict, so nothing
+ 			 * could be found for NULL key.
+ 			 */
+ 			if (skey->sk_flags & SK_ISNULL)
+ 			{
+ 				pfree(so->sign);
+ 				so->sign = NULL;
+ 				return 0;
+ 			}
+ 
+ 			/* Add next value to the signature */
+ 			signValue(&so->state, so->sign, skey->sk_argument,
+ 					  skey->sk_attno - 1);
+ 
+ 			skey++;
+ 		}
+ 	}
+ 
+ 	/*
+ 	 * We're going to read the whole index. This is why we use appropriate
+ 	 * buffer access strategy.
+ 	 */
+ 	bas = GetAccessStrategy(BAS_BULKREAD);
+ 	npages = RelationGetNumberOfBlocks(scan->indexRelation);
+ 
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		Buffer 			buffer;
+ 		Page			page;
+ 
+ 		buffer = ReadBufferExtended(scan->indexRelation, MAIN_FORKNUM,
+ 									blkno, RBM_NORMAL, bas);
+ 
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 		page = BufferGetPage(buffer);
+ 
+ 		if (!BloomPageIsDeleted(page))
+ 		{
+ 			OffsetNumber offset, maxOffset = BloomPageGetMaxOffset(page);
+ 
+ 			for (offset = 1; offset <= maxOffset; offset++)
+ 			{
+ 				BloomTuple *itup = BloomPageGetTuple(&so->state, page, offset);
+ 				bool		res = true;
+ 
+ 				/* Check index signature with scan signature */
+ 				for (i = 0; res && i < so->state.opts->bloomLength; i++)
+ 				{
+ 					if ((itup->sign[i] & so->sign[i]) != so->sign[i])
+ 						res = false;
+ 				}
+ 
+ 				/* Add matching tuples to bitmap */
+ 				if (res)
+ 				{
+ 					tbm_add_tuples(tbm, &itup->heapPtr, 1, true);
+ 					ntids++;
+ 				}
+ 			}
+ 		}
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 		CHECK_FOR_INTERRUPTS();
+ 	}
+ 	FreeAccessStrategy(bas);
+ 
+ 	return ntids;
+ }
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
new file mode 100644
index ...a7ec0c5
*** a/contrib/bloom/blutils.c
--- b/contrib/bloom/blutils.c
***************
*** 0 ****
--- 1,390 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blutils.c
+  *		Bloom index utilities.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blutils.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/amapi.h"
+ #include "access/generic_xlog.h"
+ #include "catalog/index.h"
+ #include "storage/lmgr.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "utils/memutils.h"
+ #include "access/reloptions.h"
+ #include "storage/freespace.h"
+ #include "storage/indexfsm.h"
+ 
+ #include "bloom.h"
+ 
+ /* Signature dealing macros */
+ #define BITSIGNTYPE	(BITS_PER_BYTE * sizeof(SignType))
+ #define GETWORD(x,i) ( *( (SignType*)(x) + (int)( (i) / BITSIGNTYPE ) ) )
+ #define CLRBIT(x,i)   GETWORD(x,i) &= ~( 0x01 << ( (i) % BITSIGNTYPE ) )
+ #define SETBIT(x,i)   GETWORD(x,i) |=  ( 0x01 << ( (i) % BITSIGNTYPE ) )
+ #define GETBIT(x,i) ( (GETWORD(x,i) >> ( (i) % BITSIGNTYPE )) & 0x01 )
+ 
+ PG_FUNCTION_INFO_V1(blhandler);
+ 
+ static relopt_kind bl_relopt_kind;
+ 
+ /*
+  * Module initialize function: initilized relation options.
+  */
+ void 
+ _PG_init(void)
+ {
+ 	int		i;
+ 	char	buf[16];
+ 
+ 	bl_relopt_kind = add_reloption_kind();
+ 
+ 	add_int_reloption(bl_relopt_kind, "length",
+ 					  "Length of signature in uint16 type", 5, 1, 256);
+ 
+ 	for(i = 0; i < INDEX_MAX_KEYS;i ++)
+ 	{
+ 		snprintf(buf, 16, "col%d", i+1);
+ 		add_int_reloption(bl_relopt_kind, buf,
+ 						  "Number of bits for corresponding column", 2, 1, 2048);
+ 	}
+ }
+ 
+ /*
+  * Bloom handler function: return IndexAmRoutine with access method parameters
+  * and callbacks.
+  */
+ Datum
+ blhandler(PG_FUNCTION_ARGS)
+ {
+ 	IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
+ 
+ 	amroutine->amstrategies = 1;
+ 	amroutine->amsupport = 1;
+ 	amroutine->amcanorder = false;
+ 	amroutine->amcanorderbyop = false;
+ 	amroutine->amcanbackward = false;
+ 	amroutine->amcanunique = false;
+ 	amroutine->amcanmulticol = true;
+ 	amroutine->amoptionalkey = true;
+ 	amroutine->amsearcharray = false;
+ 	amroutine->amsearchnulls = false;
+ 	amroutine->amstorage = false;
+ 	amroutine->amclusterable = false;
+ 	amroutine->ampredlocks = false;
+ 	amroutine->amkeytype = 0;
+ 
+ 	amroutine->aminsert = blinsert;
+ 	amroutine->ambeginscan = blbeginscan;
+ 	amroutine->amgettuple = NULL;
+ 	amroutine->amgetbitmap = blgetbitmap;
+ 	amroutine->amrescan = blrescan;
+ 	amroutine->amendscan = blendscan;
+ 	amroutine->ammarkpos = blmarkpos;
+ 	amroutine->amrestrpos = blrestrpos;
+ 	amroutine->ambuild = blbuild;
+ 	amroutine->ambuildempty = blbuildempty;
+ 	amroutine->ambulkdelete = blbulkdelete;
+ 	amroutine->amvacuumcleanup = blvacuumcleanup;
+ 	amroutine->amcanreturn = NULL;
+ 	amroutine->amcostestimate = blcostestimate;
+ 	amroutine->amoptions = bloptions;
+ 	amroutine->amvalidate = blvalidate;
+ 
+ 	PG_RETURN_POINTER(amroutine);
+ }
+ 
+ void 
+ initBloomState(BloomState *state, Relation index)
+ {
+ 	int	i;
+ 
+ 	state->nColumns = index->rd_att->natts;
+ 
+ 	/* Initialize hash function for each attribute */
+ 	for (i = 0; i < index->rd_att->natts; i++)
+ 	{
+ 		fmgr_info_copy(&(state->hashFn[i]),
+ 						index_getprocinfo(index, i + 1, BLOOM_HASH_PROC),
+ 						CurrentMemoryContext);
+ 	}
+ 
+ 	/* Inititalize amcache if needed */
+ 	if (!index->rd_amcache)
+ 	{
+ 		Buffer				buffer;
+ 		Page				page;
+ 		BloomMetaPageData	*meta;
+ 		BloomOptions		*opts;
+ 
+ 		opts = MemoryContextAlloc(index->rd_indexcxt, sizeof(BloomOptions));
+ 
+ 		buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 
+ 		page = BufferGetPage(buffer);
+ 
+ 		if (!BloomPageIsMeta(page))
+ 			elog(ERROR, "Relation is not a bloom index");
+ 		meta = BloomPageGetMeta(BufferGetPage(buffer));
+ 
+ 		if (meta->magickNumber != BLOOM_MAGICK_NUMBER)
+ 			elog(ERROR, "Relation is not a bloom index");
+ 
+ 		*opts = meta->opts;
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 
+ 		index->rd_amcache = (void *)opts;
+ 	}
+ 
+ 	state->opts = (BloomOptions *)index->rd_amcache;
+ 	state->sizeOfBloomTuple = BLOOMTUPLEHDRSZ +
+ 									sizeof(SignType) * state->opts->bloomLength; 
+ }
+ 
+ /*
+  * Add bits of given value to the signature.
+  */
+ void
+ signValue(BloomState *state, SignType *sign, Datum value, int attno)
+ {
+ 	uint32		hashVal;
+ 	int 		nBit, j;
+ 
+ 	/*
+ 	 * init generator with "column's" number to get
+ 	 * "hashed" seed for new value. We don't want to map
+ 	 * the same numbers from different columns into the same bits!
+ 	 */
+ 	srand(attno);
+ 
+ 	/*
+ 	 * Init hash sequence to map our value into bits. the same values
+ 	 * in different columns will be mapped into different bits because
+ 	 * of step above
+ 	 */
+ 	hashVal = DatumGetInt32(FunctionCall1(&state->hashFn[attno], value));
+ 	srand(hashVal ^ rand());
+ 
+ 	for (j = 0; j < state->opts->bitSize[attno]; j++)
+ 	{
+ 		/* prevent mutiple evaluation */
+ 		nBit = rand() % (state->opts->bloomLength * BITSIGNTYPE); 
+ 		SETBIT(sign, nBit);
+ 	}
+ }
+ 
+ /*
+  * Make bloom tuple from values.
+  */
+ BloomTuple *
+ BloomFormTuple(BloomState *state, ItemPointer iptr, Datum *values, bool *isnull)
+ {
+ 	int 		i;
+ 	BloomTuple *res = (BloomTuple *)palloc0(state->sizeOfBloomTuple);
+ 
+ 	res->heapPtr = *iptr;
+ 
+     /*
+ 	 * Blooming
+ 	 */
+ 	for (i = 0; i < state->nColumns; i++)
+ 	{
+ 		/* skip nulls */
+ 		if (isnull[i])
+ 			continue;
+ 
+ 		signValue(state, res->sign, values[i], i);
+ 	}
+ 
+ 	return res;
+ }
+ 
+ /*
+  * Add new bloom tuple to the page.
+  */
+ bool
+ BloomPageAddItem(BloomState *state, Page page, BloomTuple *tuple)
+ {
+ 	BloomTuple		   *itup;
+ 	BloomPageOpaque		opaque;
+ 	Pointer				ptr;
+ 
+ 	if (BloomPageGetFreeSpace(state, page) < state->sizeOfBloomTuple)
+ 		return false;
+ 
+ 	/* Copy new tuple to the end of page */
+ 	opaque = BloomPageGetOpaque(page);
+ 	itup = BloomPageGetTuple(state, page, opaque->maxoff + 1);
+ 	memcpy((Pointer)itup, (Pointer)tuple, state->sizeOfBloomTuple);
+ 
+ 	/* Adjust maxoff and pd_lower */
+ 	opaque->maxoff++;
+ 	ptr = (Pointer)BloomPageGetTuple(state, page, opaque->maxoff + 1);
+ 	((PageHeader) page)->pd_lower = ptr - page;
+ 
+ 	return true;
+ }
+ 
+ /*
+  * Allocate a new page (either by recycling, or by extending the index file)
+  * The returned buffer is already pinned and exclusive-locked
+  * Caller is responsible for initializing the page by calling BloomInitBuffer
+  */
+ Buffer
+ BloomNewBuffer(Relation index)
+ {
+ 	Buffer      buffer;
+ 	bool        needLock;
+ 
+ 	/* First, try to get a page from FSM */
+ 	for (;;)
+ 	{
+ 		BlockNumber blkno = GetFreeIndexPage(index);
+ 
+ 		if (blkno == InvalidBlockNumber)
+ 			break;
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 
+ 		/*
+ 		 * We have to guard against the possibility that someone else already
+ 		 * recycled this page; the buffer may be locked if so.
+ 		 */
+ 		if (ConditionalLockBuffer(buffer))
+ 		{
+ 			Page	page = BufferGetPage(buffer);
+ 
+ 			if (PageIsNew(page))
+ 				return buffer;  /* OK to use, if never initialized */
+ 
+ 			if (BloomPageIsDeleted(page))
+ 				return buffer;  /* OK to use */
+ 
+ 			LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ 		}
+ 
+ 		/* Can't use it, so release buffer and try again */
+ 		ReleaseBuffer(buffer);
+ 	}
+ 
+ 	/* Must extend the file */
+ 	needLock = !RELATION_IS_LOCAL(index);
+ 	if (needLock)
+ 		LockRelationForExtension(index, ExclusiveLock);
+ 
+ 	buffer = ReadBuffer(index, P_NEW);
+ 	LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 	if (needLock)
+ 		UnlockRelationForExtension(index, ExclusiveLock);
+ 
+ 	return buffer;
+ }
+ 
+ /*
+  * Initialize bloom page.
+  */
+ void
+ BloomInitPage(Page page, uint16 flags)
+ {
+ 	BloomPageOpaque opaque;
+ 
+ 	PageInit(page, BLCKSZ, sizeof(BloomPageOpaqueData));
+ 
+ 	opaque = BloomPageGetOpaque(page);
+ 	memset(opaque, 0, sizeof(BloomPageOpaqueData));
+ 	opaque->maxoff = 0;
+ 	opaque->flags = flags;
+ }
+ 
+ static BloomOptions *
+ makeDefaultBloomOptions(BloomOptions *opts)
+ {
+ 	int i;
+ 
+ 	if (!opts)
+ 		opts = palloc0(sizeof(BloomOptions));
+ 
+ 	if (opts->bloomLength <= 0)
+ 		opts->bloomLength = 5;
+ 
+ 	for(i = 0; i < INDEX_MAX_KEYS; i++)
+ 		if (opts->bitSize[i] <= 0
+ 				|| opts->bitSize[i] >= opts->bloomLength * sizeof(SignType))
+ 			opts->bitSize[i] = 2;
+ 
+ 	return opts;
+ }
+ 
+ /*
+  * Initialize metapage for bloom index.
+  */
+ void
+ BloomInitMetapage(Relation index)
+ {
+ 	Page				metaPage;
+ 	Buffer				metaBuffer;
+ 	BloomMetaPageData  *metadata;
+ 
+ 	metaBuffer = BloomNewBuffer(index);
+ 	Assert(BufferGetBlockNumber(metaBuffer) == BLOOM_METAPAGE_BLKNO);
+ 
+ 	GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(metaBuffer, true);
+ 
+ 	BloomInitPage(metaPage, BLOOM_META);
+ 	metadata = BloomPageGetMeta(metaPage);
+ 	memset(metadata, 0, sizeof(BloomMetaPageData));
+ 	metadata->magickNumber = BLOOM_MAGICK_NUMBER;
+ 	metadata->opts = *makeDefaultBloomOptions((BloomOptions*)index->rd_options);
+ 	((PageHeader) metaPage)->pd_lower += sizeof(BloomMetaPageData);
+ 
+ 	GenericXLogFinish();
+ 	UnlockReleaseBuffer(metaBuffer);
+ }
+ 
+ /*
+  * Initialize options for bloom index.
+  */
+ bytea *
+ bloptions(Datum reloptions, bool validate)
+ {
+ 	relopt_value	   *options;
+ 	int					numoptions;
+ 	BloomOptions	   *rdopts;
+ 	relopt_parse_elt	tab[INDEX_MAX_KEYS + 1];
+ 	int					i;
+ 	char				buf[16];
+ 
+ 	tab[0].optname = "length";
+ 	tab[0].opttype = RELOPT_TYPE_INT;
+ 	tab[0].offset = offsetof(BloomOptions, bloomLength);
+ 
+ 	for(i = 0; i < INDEX_MAX_KEYS; i++)
+ 	{
+ 		snprintf(buf, sizeof(buf), "col%d", i + 1);
+ 		tab[i + 1].optname = pstrdup(buf);
+ 		tab[i + 1].opttype = RELOPT_TYPE_INT;
+ 		tab[i + 1].offset = offsetof(BloomOptions, bitSize[i]);
+ 	}
+ 
+ 	options = parseRelOptions(reloptions, validate, bl_relopt_kind, &numoptions);
+ 	rdopts = allocateReloptStruct(sizeof(BloomOptions), options, numoptions);
+ 	fillRelOptions((void *) rdopts, sizeof(BloomOptions), options, numoptions,
+ 						validate, tab, INDEX_MAX_KEYS + 1);
+ 		
+ 	rdopts = makeDefaultBloomOptions(rdopts);
+ 
+ 	return (bytea *)rdopts;
+ }
diff --git a/contrib/bloom/blvacuum.c b/contrib/bloom/blvacuum.c
new file mode 100644
index ...c694714
*** a/contrib/bloom/blvacuum.c
--- b/contrib/bloom/blvacuum.c
***************
*** 0 ****
--- 1,195 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blvacuum.c
+  *		Bloom VACUUM functions.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blvacuum.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "catalog/storage.h"
+ #include "commands/vacuum.h"
+ #include "miscadmin.h"
+ #include "postmaster/autovacuum.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "storage/lmgr.h"
+ 
+ #include "bloom.h"
+ 
+ IndexBulkDeleteResult *
+ blbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
+ 			 IndexBulkDeleteCallback callback, void *callback_state)
+ {
+ 	Relation				index = info->index;
+ 	BlockNumber				blkno,
+ 							npages;
+ 	FreeBlockNumberArray	notFullPage;
+ 	int						countPage = 0;
+ 	BloomState				state;
+ 	Buffer					buffer;
+ 	Page					page;
+ 
+ 	if (stats == NULL)
+ 		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+ 
+ 	initBloomState(&state, index); 
+ 
+ 	/*
+ 	 * Interate over the pages. We don't care about concurrently added pages,
+ 	 * they can't contain tuples to delete.
+ 	 */
+ 	npages = RelationGetNumberOfBlocks(index);
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		BloomTuple *itup, *itupPtr, *itupEnd;
+ 
+ 		buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
+ 									RBM_NORMAL, info->strategy);
+ 
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		GenericXLogStart(index);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		if (BloomPageIsDeleted(page))
+ 		{
+ 			UnlockReleaseBuffer(buffer);
+ 			CHECK_FOR_INTERRUPTS();
+ 			continue;
+ 		}
+ 
+ 		/* Iterate over the tuples */
+ 		itup = BloomPageGetTuple(&state, page, 1);
+ 		itupPtr = BloomPageGetTuple(&state, page, 1);
+ 		itupEnd = BloomPageGetTuple(&state, page, BloomPageGetMaxOffset(page) + 1);
+ 		while (itup < itupEnd)
+ 		{
+ 			/* Do we have to delete this tuple? */
+ 			if (callback(&itup->heapPtr, callback_state))
+ 			{
+ 				stats->tuples_removed += 1;
+ 				BloomPageGetOpaque(page)->maxoff--;
+ 			} 
+ 			else 
+ 			{
+ 				if (itupPtr != itup)
+ 				{
+ 					/*
+ 					 * If we already delete something before, we have to move
+ 					 * this tuple backward.
+ 					 */
+ 					memmove((Pointer)itupPtr, (Pointer)itup,
+ 							state.sizeOfBloomTuple);
+ 				}
+ 				stats->num_index_tuples++;
+ 				itupPtr = BloomPageGetNextTuple(&state, itupPtr);
+ 			}
+ 
+ 			itup = BloomPageGetNextTuple(&state, itup);
+ 		}
+ 
+ 		/* Did we delete something? */
+ 		if (itupPtr != itup)
+ 		{
+ 			/* Is it empty page now? */
+ 			if (itupPtr == BloomPageGetData(page))
+ 				BloomPageSetDeleted(page);
+ 			/* Adjust pg_lower */
+ 			((PageHeader) page)->pd_lower = (Pointer)itupPtr - page;
+ 			/* Finish WAL-logging */
+ 			GenericXLogFinish();
+ 		}
+ 		else
+ 		{
+ 			/* Didn't change anything: abort WAL-logging */
+ 			GenericXLogAbort();
+ 		}
+ 
+ 		if (!BloomPageIsDeleted(page) && 
+ 				BloomPageGetFreeSpace(&state, page) > state.sizeOfBloomTuple && 
+ 				countPage < BloomMetaBlockN)
+ 			notFullPage[countPage++] = blkno;
+ 		UnlockReleaseBuffer(buffer);
+ 		CHECK_FOR_INTERRUPTS();
+ 	}
+ 
+ 	if (countPage > 0)
+ 	{
+ 		BloomMetaPageData	*metaData;
+ 
+ 		buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 		GenericXLogStart(index);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		metaData = BloomPageGetMeta(page);
+ 		memcpy(metaData->notFullPage, notFullPage, sizeof(FreeBlockNumberArray));
+ 		metaData->nStart=0;
+ 		metaData->nEnd = countPage;
+ 
+ 		GenericXLogFinish();
+ 		UnlockReleaseBuffer(buffer);
+ 	}
+ 
+ 	return stats;
+ }
+ 
+ IndexBulkDeleteResult *
+ blvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
+ {
+ 	Relation	index = info->index;
+ 	BlockNumber npages,
+ 				blkno;
+ 	BlockNumber	totFreePages;
+ 
+ 	if (info->analyze_only)
+ 		return stats;
+ 
+ 	if (stats == NULL)
+ 		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+ 
+ 	/*
+ 	 * Iterate over the pages: insert deleted pages into FSM and collect
+ 	 * statistics.
+ 	 */
+ 	npages = RelationGetNumberOfBlocks(index);
+ 	totFreePages = 0;
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		Buffer		buffer;
+ 		Page		page;
+ 
+ 		vacuum_delay_point();
+ 
+ 		buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
+ 									RBM_NORMAL, info->strategy);
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 		page = (Page) BufferGetPage(buffer);
+ 
+ 		if (BloomPageIsDeleted(page))
+ 		{
+ 			RecordFreeIndexPage(index, blkno);
+ 			totFreePages++;
+ 		}
+ 		else
+ 		{
+ 			stats->num_index_tuples += BloomPageGetMaxOffset(page);
+ 			stats->estimated_count += BloomPageGetMaxOffset(page);
+ 		}
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 	}
+ 
+ 	IndexFreeSpaceMapVacuum(info->index);
+ 	stats->pages_free = totFreePages;
+ 	stats->num_pages = RelationGetNumberOfBlocks(index);
+ 
+ 	return stats;
+ }
diff --git a/contrib/bloom/blvalidate.c b/contrib/bloom/blvalidate.c
new file mode 100644
index ...5344b81
*** a/contrib/bloom/blvalidate.c
--- b/contrib/bloom/blvalidate.c
***************
*** 0 ****
--- 1,220 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blvalidate.c
+  *	  Opclass validator for bloom.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blvalidate.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/amvalidate.h"
+ #include "access/htup_details.h"
+ #include "catalog/pg_amop.h"
+ #include "catalog/pg_amproc.h"
+ #include "catalog/pg_opclass.h"
+ #include "catalog/pg_opfamily.h"
+ #include "catalog/pg_type.h"
+ #include "utils/builtins.h"
+ #include "utils/lsyscache.h"
+ #include "utils/syscache.h"
+ 
+ #include "bloom.h"
+ 
+ /*
+  * Validator for a bloom opclass.
+  */
+ bool
+ blvalidate(Oid opclassoid)
+ {
+ 	bool		result = true;
+ 	HeapTuple	classtup;
+ 	Form_pg_opclass classform;
+ 	Oid			opfamilyoid;
+ 	Oid			opcintype;
+ 	Oid			opckeytype;
+ 	char	   *opclassname;
+ 	HeapTuple	familytup;
+ 	Form_pg_opfamily familyform;
+ 	char	   *opfamilyname;
+ 	CatCList   *proclist,
+ 			   *oprlist;
+ 	List	   *grouplist;
+ 	OpFamilyOpFuncGroup *opclassgroup;
+ 	int			i;
+ 	ListCell   *lc;
+ 
+ 	/* Fetch opclass information */
+ 	classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
+ 	if (!HeapTupleIsValid(classtup))
+ 		elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
+ 	classform = (Form_pg_opclass) GETSTRUCT(classtup);
+ 
+ 	opfamilyoid = classform->opcfamily;
+ 	opcintype = classform->opcintype;
+ 	opckeytype = classform->opckeytype;
+ 	if (!OidIsValid(opckeytype))
+ 		opckeytype = opcintype;
+ 	opclassname = NameStr(classform->opcname);
+ 
+ 	/* Fetch opfamily information */
+ 	familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
+ 	if (!HeapTupleIsValid(familytup))
+ 		elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
+ 	familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
+ 
+ 	opfamilyname = NameStr(familyform->opfname);
+ 
+ 	/* Fetch all operators and support functions of the opfamily */
+ 	oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
+ 	proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
+ 
+ 	/* Check individual support functions */
+ 	for (i = 0; i < proclist->n_members; i++)
+ 	{
+ 		HeapTuple	proctup = &proclist->members[i]->tuple;
+ 		Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
+ 		bool		ok;
+ 
+ 		/*
+ 		 * All bloom support functions should be registered with matching
+ 		 * left/right types
+ 		 */
+ 		if (procform->amproclefttype != procform->amprocrighttype)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains support procedure %s with cross-type registration",
+ 							opfamilyname,
+ 							format_procedure(procform->amproc))));
+ 			result = false;
+ 		}
+ 
+ 		/*
+ 		 * We can't check signatures except within the specific opclass, since
+ 		 * we need to know the associated opckeytype in many cases.
+ 		 */
+ 		if (procform->amproclefttype != opcintype)
+ 			continue;
+ 
+ 		/* Check procedure numbers and function signatures */
+ 		switch (procform->amprocnum)
+ 		{
+ 			case BLOOM_HASH_PROC:
+ 				ok = check_amproc_signature(procform->amproc, INT4OID, false,
+ 											1, 1, opckeytype);
+ 				break;
+ 			default:
+ 				ereport(INFO,
+ 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 						 errmsg("bloom opfamily %s contains function %s with invalid support number %d",
+ 								opfamilyname,
+ 								format_procedure(procform->amproc),
+ 								procform->amprocnum)));
+ 				result = false;
+ 				continue;		/* don't want additional message */
+ 		}
+ 
+ 		if (!ok)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("gist opfamily %s contains function %s with wrong signature for support number %d",
+ 							opfamilyname,
+ 							format_procedure(procform->amproc),
+ 							procform->amprocnum)));
+ 			result = false;
+ 		}
+ 	}
+ 
+ 	/* Check individual operators */
+ 	for (i = 0; i < oprlist->n_members; i++)
+ 	{
+ 		HeapTuple	oprtup = &oprlist->members[i]->tuple;
+ 		Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
+ 
+ 		/* Check it's allowed strategy for bloom */
+ 		if (oprform->amopstrategy < 1 || 
+ 			oprform->amopstrategy > BLOOM_NSTRATEGIES)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains operator %s with invalid strategy number %d",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr),
+ 							oprform->amopstrategy)));
+ 			result = false;
+ 		}
+ 
+ 		/* bloom doesn't support ORDER BY operators */
+ 		if (oprform->amoppurpose != AMOP_SEARCH ||
+ 			OidIsValid(oprform->amopsortfamily))
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains invalid ORDER BY specification for operator %s",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr))));
+ 			result = false;
+ 		}
+ 
+ 		/* Check operator signature --- same for all bloom strategies */
+ 		if (!check_amop_signature(oprform->amopopr, BOOLOID,
+ 								  oprform->amoplefttype,
+ 								  oprform->amoprighttype))
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains operator %s with wrong signature",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr))));
+ 			result = false;
+ 		}
+ 	}
+ 
+ 	/* Now check for inconsistent groups of operators/functions */
+ 	grouplist = identify_opfamily_groups(oprlist, proclist);
+ 	opclassgroup = NULL;
+ 	foreach(lc, grouplist)
+ 	{
+ 		OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
+ 
+ 		/* Remember the group exactly matching the test opclass */
+ 		if (thisgroup->lefttype == opcintype &&
+ 			thisgroup->righttype == opcintype)
+ 			opclassgroup = thisgroup;
+ 
+ 		/*
+ 		 * There is not a lot we can do to check the operator sets, since each
+ 		 * bloom opclass is more or less a law unto itself, and some contain
+ 		 * only operators that are binary-compatible with the opclass datatype
+ 		 * (meaning that empty operator sets can be OK).  That case also means
+ 		 * that we shouldn't insist on nonempty function sets except for the
+ 		 * opclass's own group.
+ 		 */
+ 	}
+ 
+ 	/* Check that the originally-named opclass is complete */
+ 	for (i = 1; i <= BLOOM_NPROC; i++)
+ 	{
+ 		if (opclassgroup &&
+ 			(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
+ 			continue;			/* got it */
+ 		ereport(INFO,
+ 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 				 errmsg("bloom opclass %s is missing support function %d",
+ 						opclassname, i)));
+ 		result = false;
+ 	}
+ 
+ 	ReleaseCatCacheList(proclist);
+ 	ReleaseCatCacheList(oprlist);
+ 	ReleaseSysCache(familytup);
+ 	ReleaseSysCache(classtup);
+ 
+ 	return result;
+ }
diff --git a/contrib/bloom/data/data b/contrib/bloom/data/data
new file mode 100644
index ...eacf3e7
*** a/contrib/bloom/data/data
--- b/contrib/bloom/data/data
***************
*** 0 ****
--- 1,10000 ----
+ 739	3
+ 475	9
+ 45	6
+ 433	1
+ 948	8
+ 926	8
+ 397	7
+ 980	4
+ 212	5
+ 522	9
+ 74	8
+ 77	4
+ 378	9
+ 575	3
+ 625	2
+ 407	4
+ 509	9
+ 252	6
+ 487	7
+ 656	4
+ 485	6
+ 275	9
+ 285	3
+ 277	5
+ 804	8
+ 424	9
+ 553	5
+ 245	9
+ 384	8
+ 202	0
+ 43	6
+ 374	6
+ 490	6
+ 105	10
+ 311	8
+ 411	8
+ 343	7
+ 678	6
+ 942	10
+ 126	7
+ 755	6
+ 625	3
+ 52	9
+ 239	4
+ 690	4
+ 445	7
+ 49	8
+ 285	5
+ 445	4
+ 516	8
+ 151	9
+ 553	5
+ 613	2
+ 123	6
+ 187	2
+ 301	9
+ 800	9
+ 250	9
+ 796	5
+ 288	5
+ 930	7
+ 219	10
+ 553	5
+ 518	10
+ 893	0
+ 754	0
+ 960	3
+ 538	6
+ 537	7
+ 127	7
+ 910	4
+ 666	7
+ 354	9
+ 562	1
+ 405	8
+ 635	3
+ 583	9
+ 313	1
+ 358	8
+ 133	3
+ 864	9
+ 296	8
+ 193	8
+ 396	7
+ 495	5
+ 454	4
+ 952	1
+ 115	3
+ 36	7
+ 455	4
+ 527	1
+ 775	1
+ 945	1
+ 246	3
+ 920	4
+ 554	8
+ 267	8
+ 608	5
+ 684	0
+ 190	2
+ 527	6
+ 584	5
+ 764	7
+ 785	8
+ 376	2
+ 240	9
+ 330	0
+ 13	3
+ 103	3
+ 578	0
+ 639	1
+ 807	9
+ 982	4
+ 365	7
+ 418	6
+ 844	9
+ 199	4
+ 425	10
+ 127	2
+ 762	5
+ 450	0
+ 406	8
+ 17	4
+ 55	1
+ 678	6
+ 143	3
+ 764	9
+ 222	7
+ 364	6
+ 411	8
+ 142	3
+ 728	3
+ 684	2
+ 303	8
+ 363	1
+ 314	8
+ 67	7
+ 592	1
+ 139	6
+ 204	8
+ 279	3
+ 133	0
+ 296	4
+ 788	7
+ 942	2
+ 441	1
+ 454	2
+ 424	1
+ 322	7
+ 949	7
+ 793	3
+ 497	9
+ 984	1
+ 944	1
+ 736	1
+ 940	0
+ 494	1
+ 57	8
+ 429	8
+ 449	4
+ 44	9
+ 454	5
+ 60	9
+ 636	4
+ 606	6
+ 67	4
+ 848	6
+ 259	8
+ 654	2
+ 955	4
+ 351	9
+ 405	8
+ 968	5
+ 634	4
+ 308	1
+ 767	4
+ 974	2
+ 850	0
+ 99	5
+ 416	7
+ 71	5
+ 103	9
+ 48	4
+ 750	7
+ 565	7
+ 92	9
+ 599	5
+ 760	6
+ 960	4
+ 964	3
+ 478	7
+ 620	5
+ 953	5
+ 485	1
+ 957	9
+ 756	0
+ 383	9
+ 946	4
+ 222	7
+ 133	8
+ 401	2
+ 702	0
+ 723	5
+ 568	7
+ 857	5
+ 951	3
+ 264	6
+ 786	2
+ 42	3
+ 268	10
+ 172	0
+ 26	6
+ 884	10
+ 986	1
+ 667	1
+ 893	1
+ 344	6
+ 68	1
+ 58	6
+ 750	9
+ 168	7
+ 249	4
+ 273	0
+ 649	3
+ 306	9
+ 314	5
+ 942	3
+ 33	8
+ 311	0
+ 932	10
+ 138	8
+ 47	5
+ 420	1
+ 550	5
+ 751	3
+ 392	9
+ 1	6
+ 351	3
+ 677	10
+ 588	10
+ 917	9
+ 461	9
+ 242	5
+ 685	6
+ 514	6
+ 531	7
+ 442	6
+ 135	9
+ 693	7
+ 341	4
+ 984	7
+ 362	10
+ 375	7
+ 259	1
+ 713	8
+ 35	6
+ 750	5
+ 489	10
+ 991	2
+ 544	5
+ 791	1
+ 156	2
+ 653	3
+ 96	3
+ 976	4
+ 789	10
+ 170	2
+ 946	5
+ 865	2
+ 597	6
+ 53	6
+ 209	8
+ 128	7
+ 794	1
+ 871	3
+ 623	7
+ 413	8
+ 895	1
+ 70	10
+ 411	0
+ 428	2
+ 6	6
+ 352	10
+ 143	2
+ 156	7
+ 795	2
+ 372	0
+ 11	5
+ 701	8
+ 619	6
+ 142	2
+ 233	6
+ 20	1
+ 621	1
+ 118	0
+ 136	5
+ 232	1
+ 145	6
+ 93	3
+ 800	2
+ 28	6
+ 457	4
+ 598	5
+ 900	3
+ 273	5
+ 870	4
+ 760	1
+ 970	8
+ 231	6
+ 871	3
+ 622	0
+ 895	9
+ 148	0
+ 439	2
+ 328	2
+ 489	4
+ 834	9
+ 756	4
+ 415	7
+ 730	7
+ 175	6
+ 102	9
+ 703	1
+ 715	9
+ 662	6
+ 283	3
+ 592	2
+ 139	7
+ 218	6
+ 981	5
+ 816	5
+ 902	7
+ 417	7
+ 82	8
+ 314	8
+ 519	5
+ 413	6
+ 424	1
+ 693	1
+ 50	4
+ 725	3
+ 639	3
+ 511	8
+ 58	7
+ 356	0
+ 275	2
+ 510	2
+ 822	9
+ 835	9
+ 759	1
+ 717	3
+ 639	1
+ 899	1
+ 246	6
+ 202	3
+ 946	9
+ 629	6
+ 245	1
+ 363	3
+ 870	7
+ 342	1
+ 891	9
+ 322	7
+ 778	2
+ 617	5
+ 307	3
+ 814	9
+ 463	7
+ 8	7
+ 304	2
+ 5	2
+ 138	6
+ 835	4
+ 774	2
+ 685	6
+ 916	0
+ 789	8
+ 878	1
+ 519	7
+ 269	1
+ 193	6
+ 470	0
+ 522	9
+ 720	5
+ 643	0
+ 741	6
+ 275	9
+ 282	1
+ 261	1
+ 306	9
+ 701	2
+ 973	5
+ 28	9
+ 602	5
+ 507	9
+ 683	7
+ 448	2
+ 708	10
+ 88	4
+ 501	7
+ 453	2
+ 379	7
+ 120	7
+ 836	4
+ 717	1
+ 327	4
+ 365	3
+ 908	4
+ 151	5
+ 940	7
+ 381	6
+ 359	8
+ 777	1
+ 799	9
+ 495	3
+ 595	9
+ 542	10
+ 675	7
+ 634	5
+ 44	4
+ 654	4
+ 769	0
+ 671	7
+ 411	8
+ 187	4
+ 481	6
+ 974	8
+ 398	8
+ 907	2
+ 615	4
+ 497	2
+ 349	0
+ 183	0
+ 701	8
+ 535	7
+ 169	2
+ 116	9
+ 208	8
+ 615	6
+ 610	8
+ 970	1
+ 371	9
+ 931	8
+ 695	8
+ 966	3
+ 239	5
+ 520	6
+ 502	7
+ 612	2
+ 520	1
+ 948	7
+ 337	1
+ 627	5
+ 852	2
+ 165	5
+ 45	1
+ 554	4
+ 79	5
+ 185	8
+ 323	2
+ 84	6
+ 613	6
+ 151	1
+ 306	8
+ 318	8
+ 911	3
+ 516	2
+ 331	1
+ 793	2
+ 385	10
+ 646	4
+ 92	2
+ 846	2
+ 686	0
+ 945	0
+ 181	0
+ 572	8
+ 633	7
+ 909	9
+ 486	2
+ 766	4
+ 493	3
+ 645	8
+ 424	4
+ 8	8
+ 395	7
+ 240	5
+ 855	1
+ 659	5
+ 117	6
+ 551	3
+ 634	1
+ 93	3
+ 846	0
+ 206	3
+ 228	10
+ 730	7
+ 253	4
+ 546	7
+ 813	6
+ 487	2
+ 209	7
+ 696	1
+ 814	4
+ 605	9
+ 959	2
+ 230	6
+ 278	3
+ 860	1
+ 324	1
+ 457	6
+ 37	2
+ 273	3
+ 561	8
+ 968	4
+ 373	5
+ 582	6
+ 183	3
+ 646	10
+ 633	3
+ 928	6
+ 407	2
+ 185	7
+ 480	0
+ 810	8
+ 111	3
+ 355	1
+ 453	6
+ 439	0
+ 447	4
+ 388	8
+ 862	10
+ 402	0
+ 247	0
+ 42	9
+ 299	10
+ 472	7
+ 127	7
+ 392	6
+ 702	2
+ 410	8
+ 468	8
+ 960	9
+ 394	4
+ 935	8
+ 806	3
+ 661	7
+ 292	1
+ 714	5
+ 111	8
+ 419	4
+ 725	9
+ 117	9
+ 548	5
+ 460	2
+ 711	9
+ 62	2
+ 636	0
+ 99	0
+ 421	0
+ 870	2
+ 357	5
+ 896	6
+ 594	6
+ 189	7
+ 365	6
+ 116	1
+ 499	2
+ 943	0
+ 742	4
+ 297	5
+ 273	4
+ 631	9
+ 382	7
+ 938	8
+ 765	8
+ 30	1
+ 338	9
+ 771	9
+ 535	10
+ 637	9
+ 568	8
+ 990	1
+ 986	9
+ 114	7
+ 335	4
+ 181	6
+ 770	8
+ 517	2
+ 543	5
+ 954	3
+ 262	10
+ 430	6
+ 910	2
+ 531	4
+ 160	2
+ 345	7
+ 921	3
+ 795	9
+ 267	9
+ 635	6
+ 319	8
+ 211	1
+ 628	7
+ 240	2
+ 182	2
+ 479	4
+ 179	9
+ 43	1
+ 110	6
+ 535	3
+ 743	9
+ 999	7
+ 214	8
+ 571	5
+ 702	2
+ 85	0
+ 22	3
+ 111	6
+ 23	4
+ 821	2
+ 546	3
+ 648	7
+ 210	7
+ 814	3
+ 267	3
+ 590	0
+ 228	6
+ 674	4
+ 382	2
+ 924	1
+ 450	0
+ 106	5
+ 304	2
+ 121	3
+ 568	9
+ 532	1
+ 243	2
+ 839	5
+ 872	7
+ 772	1
+ 2	4
+ 148	2
+ 950	8
+ 673	3
+ 66	6
+ 416	5
+ 606	5
+ 987	9
+ 739	1
+ 237	3
+ 51	8
+ 422	3
+ 949	3
+ 746	8
+ 914	5
+ 959	9
+ 880	1
+ 146	8
+ 929	8
+ 163	10
+ 416	6
+ 511	0
+ 102	5
+ 932	8
+ 606	2
+ 149	7
+ 939	6
+ 951	9
+ 831	7
+ 710	7
+ 215	7
+ 662	1
+ 777	8
+ 926	7
+ 627	1
+ 701	0
+ 668	2
+ 66	8
+ 709	10
+ 611	3
+ 168	8
+ 973	1
+ 330	9
+ 996	2
+ 620	7
+ 907	8
+ 375	6
+ 931	2
+ 377	9
+ 857	0
+ 945	6
+ 49	6
+ 769	1
+ 384	5
+ 113	10
+ 794	3
+ 756	8
+ 389	1
+ 690	4
+ 248	3
+ 90	2
+ 146	5
+ 724	1
+ 616	1
+ 933	5
+ 106	9
+ 31	2
+ 492	8
+ 270	9
+ 279	4
+ 872	1
+ 664	6
+ 840	1
+ 713	5
+ 438	10
+ 841	5
+ 116	10
+ 994	8
+ 63	6
+ 942	10
+ 83	0
+ 874	1
+ 203	4
+ 914	5
+ 242	2
+ 856	1
+ 265	5
+ 742	1
+ 573	5
+ 635	0
+ 416	5
+ 540	5
+ 462	5
+ 373	5
+ 143	3
+ 520	2
+ 363	4
+ 340	6
+ 760	3
+ 40	0
+ 446	9
+ 117	7
+ 416	9
+ 816	10
+ 313	5
+ 0	7
+ 927	5
+ 261	4
+ 74	6
+ 914	2
+ 949	4
+ 443	3
+ 829	8
+ 879	6
+ 37	9
+ 591	5
+ 814	7
+ 195	2
+ 566	0
+ 218	9
+ 462	2
+ 608	4
+ 759	9
+ 778	8
+ 504	7
+ 49	5
+ 127	5
+ 765	10
+ 276	6
+ 544	3
+ 562	1
+ 797	4
+ 843	10
+ 605	4
+ 2	8
+ 288	5
+ 42	9
+ 853	8
+ 765	6
+ 633	3
+ 324	7
+ 722	5
+ 175	5
+ 406	5
+ 130	9
+ 765	7
+ 85	6
+ 68	9
+ 553	7
+ 337	6
+ 497	6
+ 19	5
+ 520	9
+ 340	3
+ 504	10
+ 554	8
+ 656	3
+ 279	8
+ 763	7
+ 283	9
+ 634	0
+ 585	7
+ 609	7
+ 647	2
+ 326	10
+ 717	8
+ 608	7
+ 362	1
+ 608	7
+ 413	1
+ 676	10
+ 940	3
+ 244	2
+ 163	0
+ 903	4
+ 899	5
+ 494	5
+ 256	1
+ 136	9
+ 264	5
+ 886	10
+ 285	5
+ 717	6
+ 621	3
+ 349	0
+ 436	0
+ 2	4
+ 356	2
+ 595	5
+ 251	5
+ 965	2
+ 34	5
+ 633	3
+ 562	8
+ 192	8
+ 231	1
+ 807	5
+ 571	5
+ 163	2
+ 848	5
+ 226	3
+ 536	2
+ 661	9
+ 473	3
+ 412	7
+ 753	4
+ 874	8
+ 837	5
+ 77	4
+ 277	3
+ 225	5
+ 347	0
+ 24	9
+ 555	2
+ 109	4
+ 699	3
+ 688	2
+ 563	3
+ 128	0
+ 604	5
+ 759	4
+ 919	6
+ 143	8
+ 141	2
+ 154	4
+ 488	4
+ 926	8
+ 410	10
+ 752	10
+ 137	9
+ 369	8
+ 197	1
+ 72	8
+ 405	2
+ 795	0
+ 741	6
+ 365	7
+ 187	5
+ 415	3
+ 728	6
+ 745	2
+ 948	7
+ 51	4
+ 621	8
+ 324	8
+ 665	7
+ 595	9
+ 750	7
+ 622	2
+ 867	4
+ 164	6
+ 971	5
+ 267	2
+ 38	7
+ 485	8
+ 251	2
+ 982	2
+ 902	0
+ 556	5
+ 836	9
+ 282	5
+ 573	9
+ 364	3
+ 543	10
+ 477	4
+ 403	6
+ 18	4
+ 171	3
+ 531	2
+ 966	0
+ 974	2
+ 247	10
+ 415	1
+ 988	10
+ 672	8
+ 851	10
+ 325	4
+ 830	7
+ 746	4
+ 675	2
+ 784	1
+ 865	8
+ 450	0
+ 86	10
+ 244	1
+ 998	2
+ 269	2
+ 173	7
+ 394	2
+ 655	1
+ 986	5
+ 20	3
+ 930	8
+ 0	7
+ 224	7
+ 900	0
+ 752	8
+ 809	2
+ 800	9
+ 184	0
+ 947	2
+ 261	2
+ 427	4
+ 899	8
+ 596	6
+ 887	6
+ 60	9
+ 894	10
+ 757	9
+ 667	10
+ 569	6
+ 987	3
+ 331	8
+ 524	1
+ 691	7
+ 174	6
+ 891	4
+ 854	3
+ 870	8
+ 139	5
+ 307	0
+ 48	4
+ 933	9
+ 358	7
+ 836	0
+ 670	4
+ 591	7
+ 726	9
+ 454	3
+ 53	1
+ 959	2
+ 783	8
+ 663	6
+ 168	5
+ 389	3
+ 999	7
+ 334	0
+ 64	3
+ 989	4
+ 957	8
+ 447	6
+ 231	0
+ 285	10
+ 960	7
+ 208	0
+ 883	2
+ 240	7
+ 16	9
+ 302	2
+ 435	7
+ 490	4
+ 388	8
+ 481	5
+ 91	5
+ 874	0
+ 296	3
+ 675	5
+ 359	10
+ 484	3
+ 698	7
+ 332	6
+ 858	6
+ 247	9
+ 475	5
+ 57	9
+ 241	5
+ 344	6
+ 371	8
+ 81	5
+ 296	10
+ 509	6
+ 277	2
+ 120	6
+ 143	6
+ 955	8
+ 296	3
+ 421	2
+ 860	7
+ 28	3
+ 217	1
+ 244	5
+ 632	6
+ 87	0
+ 414	2
+ 465	7
+ 123	10
+ 303	4
+ 158	4
+ 36	3
+ 27	10
+ 142	3
+ 278	6
+ 476	1
+ 231	5
+ 472	4
+ 588	7
+ 907	2
+ 305	10
+ 223	7
+ 161	7
+ 428	3
+ 662	7
+ 684	8
+ 154	7
+ 121	2
+ 711	3
+ 503	10
+ 826	10
+ 127	1
+ 483	6
+ 506	1
+ 316	4
+ 292	6
+ 407	5
+ 339	6
+ 203	8
+ 853	9
+ 499	5
+ 684	7
+ 257	8
+ 833	10
+ 68	3
+ 958	9
+ 316	1
+ 950	8
+ 685	5
+ 870	0
+ 869	2
+ 622	3
+ 676	10
+ 844	9
+ 729	7
+ 743	2
+ 234	4
+ 881	5
+ 233	7
+ 460	3
+ 51	4
+ 193	4
+ 503	1
+ 165	2
+ 600	0
+ 189	5
+ 197	8
+ 745	9
+ 773	6
+ 752	5
+ 285	5
+ 730	5
+ 923	6
+ 10	2
+ 324	5
+ 455	4
+ 888	6
+ 741	4
+ 792	9
+ 579	4
+ 942	8
+ 862	1
+ 580	6
+ 12	4
+ 196	8
+ 854	5
+ 259	6
+ 1	2
+ 195	0
+ 336	5
+ 481	8
+ 894	4
+ 440	6
+ 760	2
+ 542	3
+ 625	5
+ 107	5
+ 622	7
+ 94	6
+ 40	3
+ 397	9
+ 771	7
+ 479	8
+ 837	7
+ 783	2
+ 192	3
+ 964	1
+ 633	4
+ 721	4
+ 637	3
+ 732	3
+ 746	8
+ 749	4
+ 527	8
+ 2	6
+ 133	4
+ 462	9
+ 54	9
+ 677	9
+ 613	5
+ 64	8
+ 725	0
+ 891	4
+ 433	6
+ 751	1
+ 876	5
+ 332	6
+ 324	1
+ 990	9
+ 925	10
+ 418	1
+ 390	9
+ 962	4
+ 820	6
+ 335	4
+ 99	4
+ 239	8
+ 427	1
+ 182	9
+ 743	9
+ 930	6
+ 418	3
+ 241	7
+ 344	2
+ 593	3
+ 223	0
+ 326	6
+ 891	3
+ 58	7
+ 928	4
+ 145	0
+ 792	4
+ 851	2
+ 514	0
+ 80	3
+ 967	0
+ 875	4
+ 272	1
+ 126	6
+ 347	7
+ 884	6
+ 730	2
+ 184	6
+ 498	2
+ 333	4
+ 635	5
+ 453	4
+ 861	3
+ 647	4
+ 338	7
+ 632	3
+ 736	5
+ 689	0
+ 624	8
+ 623	10
+ 534	5
+ 541	3
+ 717	7
+ 885	2
+ 967	2
+ 641	6
+ 696	1
+ 29	6
+ 399	7
+ 933	7
+ 403	6
+ 41	1
+ 73	7
+ 147	7
+ 546	8
+ 668	1
+ 278	2
+ 344	10
+ 934	2
+ 209	9
+ 447	8
+ 503	1
+ 944	5
+ 700	3
+ 208	6
+ 79	6
+ 198	1
+ 750	3
+ 851	9
+ 967	4
+ 668	6
+ 477	9
+ 843	8
+ 940	8
+ 51	1
+ 678	5
+ 999	2
+ 641	9
+ 713	3
+ 285	9
+ 974	4
+ 532	2
+ 485	3
+ 442	3
+ 179	4
+ 733	8
+ 44	2
+ 792	9
+ 32	7
+ 664	1
+ 880	3
+ 581	9
+ 523	2
+ 822	2
+ 563	1
+ 157	5
+ 471	7
+ 709	10
+ 971	2
+ 292	2
+ 561	0
+ 997	6
+ 236	8
+ 491	3
+ 521	2
+ 351	4
+ 498	9
+ 281	0
+ 153	1
+ 258	7
+ 209	4
+ 253	7
+ 105	10
+ 636	1
+ 113	9
+ 227	7
+ 954	2
+ 278	2
+ 14	8
+ 459	5
+ 926	8
+ 937	4
+ 742	2
+ 446	9
+ 320	7
+ 611	5
+ 120	9
+ 210	2
+ 827	8
+ 301	9
+ 775	5
+ 614	7
+ 753	9
+ 918	8
+ 663	4
+ 302	6
+ 187	2
+ 13	9
+ 457	5
+ 824	8
+ 163	4
+ 307	3
+ 300	5
+ 508	1
+ 363	8
+ 67	1
+ 338	7
+ 866	1
+ 573	8
+ 858	2
+ 161	2
+ 824	3
+ 399	8
+ 277	9
+ 295	1
+ 633	5
+ 536	9
+ 742	8
+ 456	3
+ 963	8
+ 61	0
+ 956	4
+ 710	8
+ 490	3
+ 606	3
+ 519	8
+ 508	3
+ 116	9
+ 179	4
+ 762	5
+ 494	4
+ 934	0
+ 335	7
+ 867	8
+ 926	8
+ 610	10
+ 859	6
+ 386	6
+ 389	9
+ 852	10
+ 224	4
+ 763	7
+ 713	9
+ 638	9
+ 272	4
+ 367	8
+ 796	3
+ 796	1
+ 977	7
+ 921	9
+ 493	5
+ 890	4
+ 98	3
+ 921	5
+ 152	8
+ 482	4
+ 143	2
+ 108	9
+ 124	7
+ 750	4
+ 147	1
+ 162	9
+ 418	10
+ 73	4
+ 622	10
+ 298	1
+ 526	2
+ 466	6
+ 464	4
+ 111	6
+ 159	6
+ 992	3
+ 837	1
+ 159	10
+ 847	9
+ 357	10
+ 26	5
+ 937	4
+ 478	0
+ 839	1
+ 5	1
+ 214	5
+ 325	7
+ 156	8
+ 66	3
+ 405	2
+ 859	4
+ 527	7
+ 498	7
+ 658	3
+ 595	0
+ 339	6
+ 535	3
+ 65	0
+ 286	9
+ 112	3
+ 41	3
+ 823	4
+ 6	10
+ 154	1
+ 245	6
+ 295	1
+ 957	8
+ 800	5
+ 508	5
+ 801	1
+ 473	1
+ 723	0
+ 415	8
+ 21	7
+ 691	1
+ 993	7
+ 460	8
+ 97	5
+ 795	3
+ 536	0
+ 811	8
+ 144	8
+ 654	9
+ 224	2
+ 403	0
+ 263	9
+ 165	10
+ 884	6
+ 774	9
+ 282	5
+ 39	3
+ 197	5
+ 91	3
+ 964	9
+ 546	5
+ 926	4
+ 332	1
+ 127	10
+ 15	4
+ 146	4
+ 376	4
+ 293	5
+ 396	2
+ 120	2
+ 83	4
+ 636	1
+ 677	8
+ 620	8
+ 128	6
+ 655	7
+ 84	6
+ 32	4
+ 651	2
+ 400	7
+ 510	5
+ 83	9
+ 957	4
+ 426	4
+ 554	5
+ 523	6
+ 949	2
+ 758	6
+ 992	4
+ 395	1
+ 962	0
+ 794	0
+ 630	8
+ 461	3
+ 984	9
+ 947	5
+ 408	0
+ 380	4
+ 407	8
+ 717	10
+ 352	2
+ 598	3
+ 399	4
+ 927	4
+ 734	3
+ 510	7
+ 371	3
+ 742	0
+ 129	2
+ 283	1
+ 63	2
+ 608	5
+ 261	10
+ 835	7
+ 793	6
+ 628	1
+ 793	2
+ 446	2
+ 582	4
+ 583	3
+ 695	1
+ 13	1
+ 397	8
+ 68	5
+ 957	4
+ 641	0
+ 582	2
+ 491	8
+ 235	3
+ 510	0
+ 879	1
+ 173	7
+ 365	6
+ 863	9
+ 992	4
+ 264	7
+ 540	3
+ 754	9
+ 32	8
+ 464	10
+ 174	1
+ 9	8
+ 353	5
+ 598	6
+ 827	1
+ 616	7
+ 247	8
+ 377	6
+ 407	2
+ 558	4
+ 686	8
+ 86	2
+ 99	8
+ 163	1
+ 662	6
+ 120	8
+ 731	1
+ 591	1
+ 630	2
+ 671	5
+ 298	3
+ 162	5
+ 75	5
+ 155	5
+ 779	7
+ 880	5
+ 535	10
+ 691	6
+ 806	9
+ 764	5
+ 480	9
+ 303	2
+ 13	9
+ 294	6
+ 84	10
+ 100	4
+ 252	3
+ 926	3
+ 801	1
+ 808	6
+ 794	7
+ 45	3
+ 655	7
+ 963	5
+ 589	7
+ 929	1
+ 611	2
+ 279	6
+ 127	6
+ 267	2
+ 538	4
+ 592	8
+ 629	5
+ 117	4
+ 599	9
+ 10	4
+ 614	1
+ 722	3
+ 790	7
+ 730	4
+ 413	7
+ 447	0
+ 891	7
+ 648	0
+ 299	9
+ 228	8
+ 282	8
+ 627	9
+ 338	7
+ 340	9
+ 669	3
+ 330	3
+ 404	1
+ 552	2
+ 738	3
+ 574	2
+ 941	0
+ 174	8
+ 747	8
+ 849	0
+ 738	1
+ 884	0
+ 897	5
+ 931	2
+ 256	3
+ 173	9
+ 621	5
+ 209	0
+ 556	8
+ 220	3
+ 43	8
+ 444	10
+ 815	6
+ 816	6
+ 441	7
+ 609	2
+ 742	5
+ 199	6
+ 4	1
+ 875	3
+ 400	0
+ 185	0
+ 551	4
+ 46	1
+ 155	3
+ 400	2
+ 60	8
+ 183	9
+ 463	10
+ 436	9
+ 665	0
+ 82	4
+ 538	3
+ 47	5
+ 410	9
+ 802	8
+ 970	10
+ 832	5
+ 381	9
+ 627	5
+ 145	0
+ 734	2
+ 872	9
+ 79	3
+ 916	5
+ 238	6
+ 560	3
+ 988	1
+ 602	0
+ 639	0
+ 956	4
+ 823	9
+ 429	7
+ 446	8
+ 533	1
+ 346	7
+ 101	1
+ 883	10
+ 997	10
+ 307	9
+ 477	5
+ 495	0
+ 865	5
+ 135	5
+ 517	8
+ 479	5
+ 215	3
+ 399	6
+ 957	8
+ 454	5
+ 919	8
+ 168	0
+ 880	1
+ 992	9
+ 13	3
+ 791	5
+ 844	3
+ 527	7
+ 768	7
+ 176	3
+ 435	7
+ 759	7
+ 957	2
+ 295	9
+ 4	7
+ 403	9
+ 548	6
+ 943	4
+ 622	9
+ 305	6
+ 235	1
+ 124	1
+ 381	7
+ 789	1
+ 312	10
+ 434	7
+ 619	2
+ 398	6
+ 351	7
+ 489	4
+ 442	9
+ 279	10
+ 463	2
+ 418	1
+ 158	7
+ 720	4
+ 819	8
+ 473	2
+ 496	3
+ 349	8
+ 226	8
+ 556	8
+ 976	10
+ 421	3
+ 648	9
+ 683	1
+ 803	10
+ 80	3
+ 184	5
+ 352	3
+ 221	1
+ 736	0
+ 917	2
+ 240	4
+ 470	6
+ 221	7
+ 372	8
+ 542	3
+ 731	10
+ 676	4
+ 874	4
+ 469	7
+ 321	5
+ 943	5
+ 46	3
+ 848	3
+ 367	6
+ 307	3
+ 793	5
+ 697	3
+ 135	9
+ 959	5
+ 695	5
+ 855	4
+ 464	5
+ 806	3
+ 890	3
+ 14	2
+ 822	10
+ 715	9
+ 253	6
+ 135	6
+ 147	4
+ 904	9
+ 988	6
+ 203	1
+ 519	2
+ 630	2
+ 663	5
+ 640	1
+ 16	4
+ 465	9
+ 720	5
+ 115	5
+ 437	8
+ 410	7
+ 393	5
+ 309	5
+ 987	2
+ 479	10
+ 814	7
+ 97	3
+ 844	7
+ 547	5
+ 212	2
+ 634	2
+ 634	1
+ 133	4
+ 579	2
+ 896	0
+ 79	3
+ 706	5
+ 852	0
+ 12	8
+ 228	5
+ 813	0
+ 173	9
+ 376	0
+ 637	9
+ 524	8
+ 111	2
+ 76	7
+ 258	2
+ 98	8
+ 457	10
+ 853	5
+ 301	6
+ 8	2
+ 574	0
+ 991	8
+ 511	8
+ 845	7
+ 713	2
+ 702	4
+ 144	2
+ 199	3
+ 385	3
+ 999	6
+ 483	1
+ 481	9
+ 91	3
+ 475	4
+ 893	5
+ 544	5
+ 503	5
+ 270	0
+ 338	1
+ 698	1
+ 336	4
+ 402	5
+ 626	6
+ 735	0
+ 875	7
+ 655	4
+ 830	1
+ 298	9
+ 469	8
+ 313	4
+ 256	9
+ 830	8
+ 392	1
+ 773	7
+ 215	5
+ 782	6
+ 871	2
+ 31	5
+ 784	8
+ 509	7
+ 499	2
+ 17	3
+ 299	3
+ 250	8
+ 89	6
+ 130	3
+ 421	10
+ 104	8
+ 59	9
+ 543	3
+ 348	3
+ 824	2
+ 508	9
+ 717	3
+ 620	2
+ 950	1
+ 390	10
+ 448	7
+ 282	7
+ 457	4
+ 262	6
+ 716	7
+ 546	8
+ 496	6
+ 697	0
+ 879	0
+ 363	7
+ 265	9
+ 557	10
+ 163	2
+ 209	1
+ 296	6
+ 80	7
+ 288	4
+ 442	7
+ 733	7
+ 332	4
+ 387	9
+ 269	9
+ 483	10
+ 921	4
+ 12	3
+ 64	3
+ 155	6
+ 260	3
+ 799	5
+ 431	1
+ 68	5
+ 839	4
+ 873	3
+ 101	6
+ 986	4
+ 55	4
+ 311	3
+ 255	8
+ 290	2
+ 155	3
+ 460	2
+ 579	6
+ 840	8
+ 933	6
+ 308	4
+ 735	4
+ 875	6
+ 733	7
+ 855	8
+ 353	8
+ 268	4
+ 213	6
+ 732	5
+ 372	0
+ 644	5
+ 324	1
+ 746	9
+ 718	6
+ 743	7
+ 225	1
+ 15	10
+ 428	9
+ 534	2
+ 637	4
+ 996	10
+ 230	3
+ 399	4
+ 842	1
+ 911	2
+ 153	6
+ 741	5
+ 658	5
+ 380	4
+ 72	1
+ 28	3
+ 174	0
+ 258	6
+ 933	8
+ 763	6
+ 181	8
+ 561	4
+ 22	10
+ 854	9
+ 90	8
+ 78	2
+ 320	8
+ 719	10
+ 305	1
+ 354	4
+ 222	4
+ 675	4
+ 425	9
+ 997	4
+ 725	8
+ 928	9
+ 518	5
+ 317	5
+ 447	2
+ 405	5
+ 936	5
+ 780	3
+ 302	5
+ 233	6
+ 598	6
+ 985	8
+ 969	7
+ 215	4
+ 594	2
+ 752	3
+ 973	7
+ 224	5
+ 167	5
+ 32	6
+ 712	4
+ 152	6
+ 920	9
+ 903	2
+ 430	1
+ 830	0
+ 724	8
+ 848	7
+ 477	1
+ 88	1
+ 276	8
+ 389	2
+ 519	6
+ 740	7
+ 154	8
+ 301	9
+ 209	5
+ 514	1
+ 385	4
+ 351	8
+ 553	2
+ 843	3
+ 998	7
+ 971	5
+ 754	1
+ 545	0
+ 898	9
+ 279	4
+ 547	0
+ 104	7
+ 791	4
+ 568	10
+ 858	1
+ 129	2
+ 499	5
+ 58	1
+ 662	9
+ 330	7
+ 592	3
+ 134	3
+ 359	7
+ 376	3
+ 613	7
+ 675	2
+ 674	8
+ 862	5
+ 183	4
+ 465	0
+ 512	6
+ 284	0
+ 74	3
+ 63	7
+ 244	4
+ 395	8
+ 693	5
+ 181	1
+ 208	6
+ 309	8
+ 212	10
+ 981	9
+ 763	8
+ 352	9
+ 273	8
+ 988	8
+ 410	3
+ 796	5
+ 614	9
+ 220	9
+ 251	6
+ 693	9
+ 144	9
+ 995	4
+ 432	3
+ 174	6
+ 289	2
+ 531	1
+ 998	9
+ 997	3
+ 700	10
+ 57	1
+ 257	9
+ 594	9
+ 711	8
+ 730	10
+ 429	4
+ 905	6
+ 298	9
+ 926	7
+ 205	1
+ 374	5
+ 254	9
+ 545	3
+ 788	5
+ 524	5
+ 528	6
+ 598	8
+ 433	2
+ 656	1
+ 6	4
+ 105	4
+ 809	0
+ 8	1
+ 910	9
+ 836	1
+ 34	2
+ 608	3
+ 115	2
+ 541	9
+ 696	1
+ 391	2
+ 645	10
+ 8	1
+ 180	7
+ 220	2
+ 51	3
+ 621	9
+ 335	6
+ 966	2
+ 564	8
+ 359	6
+ 12	10
+ 887	1
+ 120	4
+ 31	8
+ 492	4
+ 40	1
+ 410	0
+ 213	6
+ 713	4
+ 777	8
+ 759	4
+ 623	1
+ 28	6
+ 338	6
+ 390	7
+ 191	4
+ 663	1
+ 530	8
+ 505	6
+ 599	10
+ 983	6
+ 133	4
+ 687	3
+ 984	4
+ 780	8
+ 163	5
+ 160	8
+ 632	2
+ 374	10
+ 780	8
+ 666	10
+ 167	3
+ 48	7
+ 112	6
+ 258	7
+ 549	2
+ 350	7
+ 635	0
+ 27	6
+ 437	8
+ 380	6
+ 345	5
+ 386	10
+ 727	8
+ 947	5
+ 525	6
+ 477	7
+ 942	5
+ 389	1
+ 77	6
+ 765	6
+ 889	1
+ 308	5
+ 153	3
+ 142	6
+ 143	5
+ 191	5
+ 62	6
+ 465	8
+ 338	4
+ 296	9
+ 25	8
+ 555	10
+ 298	9
+ 20	4
+ 591	8
+ 2	5
+ 901	3
+ 3	1
+ 645	1
+ 645	8
+ 667	8
+ 276	7
+ 413	7
+ 517	8
+ 153	8
+ 613	2
+ 586	2
+ 144	9
+ 112	2
+ 259	7
+ 949	3
+ 183	9
+ 570	2
+ 904	2
+ 331	5
+ 3	10
+ 385	3
+ 726	8
+ 20	2
+ 549	2
+ 56	2
+ 351	6
+ 330	5
+ 525	4
+ 658	8
+ 144	6
+ 45	3
+ 458	6
+ 513	4
+ 830	8
+ 911	8
+ 841	3
+ 112	6
+ 94	1
+ 810	6
+ 305	9
+ 806	7
+ 508	1
+ 150	0
+ 577	8
+ 817	7
+ 416	9
+ 49	9
+ 477	6
+ 236	3
+ 405	1
+ 140	2
+ 443	3
+ 812	5
+ 385	6
+ 181	7
+ 489	10
+ 345	10
+ 122	5
+ 30	7
+ 304	8
+ 421	7
+ 710	5
+ 594	2
+ 31	8
+ 493	4
+ 977	6
+ 681	4
+ 886	5
+ 958	3
+ 116	1
+ 960	6
+ 126	3
+ 602	2
+ 801	6
+ 948	1
+ 480	4
+ 825	2
+ 840	4
+ 376	9
+ 249	9
+ 307	2
+ 504	10
+ 646	4
+ 482	6
+ 661	6
+ 744	6
+ 203	9
+ 927	8
+ 118	7
+ 438	1
+ 833	9
+ 436	7
+ 108	3
+ 77	5
+ 147	3
+ 354	5
+ 552	9
+ 443	2
+ 248	9
+ 802	9
+ 523	5
+ 530	7
+ 416	5
+ 532	5
+ 186	10
+ 600	0
+ 887	0
+ 677	10
+ 312	8
+ 479	5
+ 80	8
+ 913	6
+ 691	4
+ 830	9
+ 281	6
+ 848	8
+ 178	4
+ 530	6
+ 835	1
+ 128	0
+ 31	7
+ 39	9
+ 765	7
+ 914	1
+ 470	4
+ 537	6
+ 226	4
+ 183	9
+ 806	0
+ 855	1
+ 645	7
+ 890	8
+ 81	4
+ 418	9
+ 482	5
+ 937	5
+ 274	10
+ 432	0
+ 692	3
+ 116	2
+ 738	7
+ 713	10
+ 102	9
+ 881	9
+ 909	7
+ 994	6
+ 439	9
+ 378	5
+ 304	8
+ 436	8
+ 341	4
+ 299	6
+ 349	7
+ 653	0
+ 76	8
+ 203	8
+ 421	9
+ 778	5
+ 812	7
+ 431	7
+ 395	4
+ 275	8
+ 309	7
+ 354	6
+ 449	8
+ 398	8
+ 163	7
+ 405	5
+ 428	1
+ 552	5
+ 828	8
+ 319	2
+ 672	1
+ 772	5
+ 756	2
+ 205	2
+ 628	5
+ 986	9
+ 134	3
+ 550	6
+ 130	9
+ 373	3
+ 644	8
+ 805	1
+ 837	4
+ 576	7
+ 113	9
+ 913	8
+ 992	7
+ 270	7
+ 889	5
+ 899	5
+ 956	9
+ 455	1
+ 225	0
+ 673	4
+ 952	0
+ 648	6
+ 825	5
+ 669	7
+ 811	2
+ 326	9
+ 140	2
+ 710	1
+ 925	10
+ 881	8
+ 454	8
+ 331	4
+ 665	8
+ 500	9
+ 791	2
+ 245	7
+ 219	9
+ 339	0
+ 347	0
+ 705	2
+ 253	0
+ 82	4
+ 270	8
+ 526	2
+ 771	4
+ 8	2
+ 186	3
+ 635	9
+ 126	1
+ 741	9
+ 307	10
+ 659	5
+ 878	10
+ 570	2
+ 5	3
+ 383	3
+ 306	5
+ 651	6
+ 256	2
+ 769	0
+ 583	8
+ 251	8
+ 117	9
+ 620	2
+ 21	4
+ 158	3
+ 346	8
+ 854	2
+ 814	4
+ 449	8
+ 699	8
+ 78	0
+ 296	7
+ 580	6
+ 905	3
+ 578	5
+ 127	8
+ 257	2
+ 715	9
+ 486	7
+ 237	6
+ 64	6
+ 461	9
+ 808	3
+ 342	3
+ 95	0
+ 89	2
+ 47	4
+ 901	6
+ 937	8
+ 977	5
+ 294	1
+ 344	6
+ 348	1
+ 428	8
+ 795	7
+ 478	9
+ 249	9
+ 777	1
+ 215	1
+ 314	3
+ 161	4
+ 482	2
+ 787	4
+ 835	7
+ 190	8
+ 238	5
+ 917	6
+ 36	3
+ 641	5
+ 100	4
+ 130	6
+ 295	4
+ 517	1
+ 436	7
+ 191	7
+ 42	4
+ 152	5
+ 559	9
+ 908	4
+ 663	1
+ 207	9
+ 583	1
+ 483	6
+ 390	1
+ 84	5
+ 561	2
+ 67	9
+ 593	6
+ 928	0
+ 316	1
+ 780	4
+ 470	9
+ 882	0
+ 871	8
+ 424	5
+ 888	6
+ 434	5
+ 756	9
+ 90	1
+ 42	2
+ 636	6
+ 387	7
+ 459	10
+ 288	4
+ 11	6
+ 505	8
+ 962	10
+ 722	8
+ 4	6
+ 634	4
+ 125	5
+ 59	6
+ 994	8
+ 476	1
+ 962	5
+ 257	6
+ 121	6
+ 301	6
+ 625	6
+ 966	6
+ 193	5
+ 426	2
+ 445	1
+ 1000	4
+ 739	6
+ 876	9
+ 157	9
+ 424	2
+ 751	9
+ 234	7
+ 418	5
+ 310	5
+ 135	6
+ 118	8
+ 200	1
+ 396	4
+ 555	8
+ 548	0
+ 969	5
+ 449	7
+ 183	3
+ 572	3
+ 261	10
+ 490	0
+ 896	7
+ 724	3
+ 214	0
+ 853	3
+ 645	10
+ 109	8
+ 56	5
+ 237	6
+ 326	8
+ 611	3
+ 334	1
+ 3	5
+ 385	6
+ 856	6
+ 571	3
+ 658	5
+ 69	4
+ 782	3
+ 415	6
+ 633	1
+ 607	7
+ 904	7
+ 248	1
+ 274	6
+ 927	9
+ 869	3
+ 945	9
+ 777	3
+ 447	6
+ 977	0
+ 978	6
+ 485	0
+ 16	3
+ 331	4
+ 902	10
+ 491	5
+ 707	4
+ 172	10
+ 537	4
+ 528	5
+ 331	4
+ 724	3
+ 268	5
+ 607	7
+ 134	6
+ 733	1
+ 219	2
+ 159	2
+ 485	5
+ 666	4
+ 455	2
+ 897	2
+ 552	1
+ 116	1
+ 515	6
+ 552	8
+ 42	3
+ 123	3
+ 777	7
+ 25	9
+ 314	8
+ 23	5
+ 975	2
+ 767	5
+ 673	4
+ 849	1
+ 591	7
+ 290	1
+ 815	4
+ 232	3
+ 51	8
+ 176	1
+ 61	3
+ 403	8
+ 28	4
+ 748	3
+ 185	8
+ 875	2
+ 953	6
+ 621	6
+ 76	5
+ 754	7
+ 216	0
+ 810	0
+ 451	0
+ 360	5
+ 826	5
+ 596	9
+ 834	10
+ 724	9
+ 426	5
+ 205	6
+ 244	1
+ 771	2
+ 724	4
+ 823	8
+ 863	6
+ 466	1
+ 622	3
+ 109	1
+ 318	5
+ 576	1
+ 6	2
+ 30	8
+ 170	8
+ 702	6
+ 226	9
+ 207	5
+ 989	10
+ 667	7
+ 372	5
+ 512	2
+ 67	10
+ 313	7
+ 254	4
+ 762	6
+ 892	3
+ 715	9
+ 510	7
+ 738	7
+ 498	4
+ 276	7
+ 348	5
+ 194	3
+ 462	9
+ 49	8
+ 350	6
+ 68	4
+ 539	4
+ 106	8
+ 804	9
+ 365	7
+ 207	1
+ 595	7
+ 824	3
+ 397	3
+ 773	7
+ 47	1
+ 156	2
+ 457	6
+ 101	5
+ 452	5
+ 66	5
+ 869	6
+ 902	10
+ 397	7
+ 844	8
+ 403	1
+ 841	10
+ 768	7
+ 330	2
+ 988	1
+ 837	0
+ 223	10
+ 276	7
+ 611	4
+ 185	1
+ 829	3
+ 583	7
+ 855	5
+ 672	3
+ 190	5
+ 14	6
+ 567	9
+ 590	3
+ 521	9
+ 498	5
+ 22	3
+ 544	2
+ 328	8
+ 925	9
+ 197	1
+ 1	0
+ 361	6
+ 723	2
+ 68	4
+ 469	3
+ 911	5
+ 851	5
+ 338	4
+ 812	9
+ 361	3
+ 368	4
+ 645	9
+ 629	10
+ 732	6
+ 911	9
+ 663	9
+ 955	0
+ 495	7
+ 241	6
+ 74	7
+ 820	10
+ 192	7
+ 462	5
+ 112	3
+ 388	5
+ 584	8
+ 856	2
+ 667	5
+ 201	4
+ 38	1
+ 329	7
+ 24	3
+ 726	5
+ 963	10
+ 81	0
+ 676	9
+ 21	9
+ 573	5
+ 398	7
+ 757	8
+ 157	3
+ 542	0
+ 569	2
+ 498	8
+ 608	5
+ 882	9
+ 238	9
+ 221	10
+ 424	2
+ 931	5
+ 221	6
+ 407	2
+ 476	10
+ 725	9
+ 664	5
+ 660	8
+ 822	2
+ 835	4
+ 411	3
+ 160	0
+ 870	0
+ 956	1
+ 947	2
+ 73	4
+ 362	0
+ 877	6
+ 612	3
+ 824	1
+ 265	5
+ 963	9
+ 31	6
+ 751	9
+ 825	6
+ 243	2
+ 920	4
+ 256	8
+ 445	2
+ 898	4
+ 390	10
+ 764	8
+ 975	6
+ 335	6
+ 926	2
+ 675	2
+ 708	6
+ 121	7
+ 261	9
+ 592	1
+ 458	8
+ 323	4
+ 238	6
+ 167	7
+ 791	1
+ 75	2
+ 36	8
+ 933	0
+ 481	3
+ 597	4
+ 427	3
+ 598	1
+ 910	7
+ 874	2
+ 590	5
+ 258	0
+ 300	6
+ 425	5
+ 160	6
+ 221	10
+ 657	3
+ 131	7
+ 134	1
+ 703	6
+ 332	3
+ 22	8
+ 573	6
+ 894	5
+ 339	8
+ 655	9
+ 234	9
+ 978	5
+ 494	4
+ 73	7
+ 995	3
+ 603	7
+ 588	7
+ 345	7
+ 799	0
+ 338	1
+ 349	4
+ 889	9
+ 980	8
+ 404	3
+ 551	1
+ 249	8
+ 972	2
+ 319	5
+ 629	4
+ 118	6
+ 685	7
+ 277	3
+ 456	6
+ 996	3
+ 670	3
+ 385	0
+ 694	3
+ 940	7
+ 57	3
+ 993	6
+ 404	2
+ 392	4
+ 468	7
+ 840	1
+ 103	10
+ 721	8
+ 680	10
+ 61	1
+ 620	1
+ 392	3
+ 391	8
+ 310	1
+ 51	3
+ 759	1
+ 595	8
+ 716	10
+ 993	1
+ 374	5
+ 819	2
+ 558	9
+ 172	3
+ 710	9
+ 278	8
+ 989	9
+ 829	4
+ 188	2
+ 158	5
+ 305	2
+ 748	1
+ 317	3
+ 815	0
+ 341	8
+ 141	7
+ 270	10
+ 929	8
+ 883	1
+ 108	6
+ 954	4
+ 364	9
+ 283	2
+ 324	5
+ 413	5
+ 970	7
+ 691	7
+ 781	0
+ 61	6
+ 41	4
+ 405	2
+ 118	7
+ 142	0
+ 504	0
+ 148	6
+ 618	1
+ 997	10
+ 46	3
+ 175	4
+ 752	6
+ 852	7
+ 306	5
+ 440	1
+ 552	5
+ 684	6
+ 904	1
+ 775	0
+ 764	9
+ 68	3
+ 942	2
+ 880	6
+ 319	9
+ 542	4
+ 157	7
+ 734	9
+ 306	6
+ 632	6
+ 130	1
+ 699	7
+ 574	4
+ 275	5
+ 472	1
+ 500	2
+ 968	6
+ 505	9
+ 785	4
+ 470	1
+ 261	0
+ 468	4
+ 730	2
+ 328	0
+ 788	10
+ 648	9
+ 32	3
+ 601	6
+ 730	9
+ 83	2
+ 926	6
+ 439	9
+ 151	9
+ 803	9
+ 327	3
+ 39	6
+ 285	5
+ 7	0
+ 709	3
+ 52	5
+ 294	7
+ 416	3
+ 47	0
+ 931	8
+ 892	0
+ 979	8
+ 597	4
+ 712	7
+ 361	5
+ 685	7
+ 789	7
+ 277	1
+ 231	3
+ 89	9
+ 618	1
+ 437	9
+ 840	9
+ 237	9
+ 869	2
+ 664	8
+ 181	6
+ 580	8
+ 61	3
+ 527	4
+ 808	2
+ 110	6
+ 936	4
+ 671	2
+ 670	8
+ 106	3
+ 901	5
+ 199	7
+ 395	4
+ 629	3
+ 603	3
+ 26	8
+ 936	6
+ 562	10
+ 898	1
+ 418	7
+ 301	5
+ 303	2
+ 915	10
+ 403	6
+ 733	5
+ 873	6
+ 52	1
+ 376	4
+ 508	0
+ 712	1
+ 297	7
+ 894	2
+ 344	5
+ 229	2
+ 546	6
+ 948	8
+ 176	3
+ 84	1
+ 225	5
+ 677	10
+ 996	5
+ 592	0
+ 622	10
+ 495	1
+ 972	2
+ 240	3
+ 944	1
+ 502	3
+ 591	7
+ 530	1
+ 379	5
+ 984	6
+ 730	1
+ 646	10
+ 555	3
+ 912	6
+ 873	5
+ 598	5
+ 472	1
+ 625	4
+ 299	9
+ 713	2
+ 1000	2
+ 531	6
+ 946	1
+ 728	3
+ 540	7
+ 881	3
+ 781	5
+ 223	3
+ 850	1
+ 885	7
+ 639	5
+ 218	1
+ 576	8
+ 555	9
+ 707	3
+ 120	7
+ 483	7
+ 298	4
+ 712	0
+ 755	3
+ 739	6
+ 521	5
+ 162	7
+ 854	0
+ 880	7
+ 735	5
+ 223	10
+ 630	8
+ 795	2
+ 676	5
+ 454	8
+ 208	9
+ 446	5
+ 367	2
+ 532	1
+ 410	3
+ 757	9
+ 789	9
+ 676	6
+ 931	6
+ 384	7
+ 74	6
+ 618	7
+ 407	4
+ 890	1
+ 915	3
+ 878	1
+ 281	3
+ 629	6
+ 482	2
+ 769	9
+ 431	5
+ 824	2
+ 445	5
+ 865	4
+ 55	2
+ 42	1
+ 855	7
+ 834	3
+ 73	7
+ 344	10
+ 68	2
+ 111	3
+ 545	7
+ 996	0
+ 901	8
+ 920	3
+ 291	7
+ 553	7
+ 243	4
+ 111	3
+ 666	2
+ 427	5
+ 812	3
+ 783	9
+ 985	1
+ 873	1
+ 348	10
+ 401	9
+ 724	4
+ 921	6
+ 163	8
+ 957	5
+ 584	5
+ 189	8
+ 928	3
+ 125	6
+ 452	6
+ 114	3
+ 814	9
+ 150	8
+ 23	0
+ 851	4
+ 7	3
+ 265	7
+ 650	2
+ 356	8
+ 25	3
+ 266	6
+ 824	5
+ 436	8
+ 754	6
+ 345	2
+ 114	5
+ 471	9
+ 356	6
+ 726	4
+ 644	6
+ 751	7
+ 829	0
+ 383	5
+ 201	7
+ 291	2
+ 53	6
+ 836	9
+ 11	3
+ 628	8
+ 834	10
+ 972	9
+ 433	4
+ 875	8
+ 63	6
+ 170	7
+ 178	9
+ 359	0
+ 937	7
+ 487	1
+ 481	8
+ 365	5
+ 335	2
+ 411	3
+ 472	0
+ 112	3
+ 13	1
+ 254	4
+ 526	1
+ 236	6
+ 730	4
+ 297	9
+ 327	7
+ 916	3
+ 399	4
+ 402	9
+ 181	8
+ 414	5
+ 967	8
+ 862	4
+ 865	10
+ 745	9
+ 58	10
+ 325	6
+ 128	6
+ 173	9
+ 967	5
+ 767	3
+ 127	7
+ 558	5
+ 86	10
+ 405	3
+ 726	8
+ 783	7
+ 645	6
+ 132	5
+ 619	9
+ 388	7
+ 876	7
+ 260	0
+ 273	4
+ 862	2
+ 903	6
+ 534	0
+ 312	1
+ 555	4
+ 51	10
+ 665	8
+ 780	4
+ 469	4
+ 93	6
+ 934	7
+ 477	3
+ 388	4
+ 34	6
+ 356	3
+ 81	2
+ 546	10
+ 847	1
+ 15	2
+ 171	6
+ 558	2
+ 531	2
+ 998	3
+ 672	5
+ 735	8
+ 67	7
+ 476	5
+ 991	9
+ 897	0
+ 512	3
+ 332	6
+ 471	9
+ 578	3
+ 958	6
+ 477	1
+ 163	0
+ 351	7
+ 259	3
+ 4	9
+ 816	7
+ 695	9
+ 409	2
+ 427	4
+ 35	3
+ 426	5
+ 576	8
+ 140	0
+ 636	7
+ 365	6
+ 311	8
+ 724	5
+ 877	1
+ 167	1
+ 424	2
+ 67	2
+ 911	8
+ 122	3
+ 932	5
+ 721	10
+ 872	1
+ 514	4
+ 905	7
+ 495	5
+ 372	9
+ 135	7
+ 702	9
+ 156	6
+ 933	3
+ 715	4
+ 495	8
+ 596	4
+ 543	7
+ 726	5
+ 267	4
+ 442	1
+ 595	10
+ 588	5
+ 610	1
+ 40	10
+ 943	2
+ 665	6
+ 34	8
+ 224	10
+ 145	9
+ 324	6
+ 721	9
+ 45	3
+ 638	8
+ 739	9
+ 219	2
+ 45	8
+ 138	6
+ 314	7
+ 717	4
+ 730	7
+ 529	4
+ 305	6
+ 216	5
+ 531	4
+ 468	9
+ 0	2
+ 776	0
+ 453	4
+ 817	2
+ 320	0
+ 373	4
+ 850	5
+ 998	2
+ 259	7
+ 518	10
+ 375	0
+ 384	7
+ 611	6
+ 209	1
+ 961	7
+ 998	10
+ 866	8
+ 7	3
+ 188	8
+ 511	5
+ 861	9
+ 873	7
+ 395	9
+ 875	7
+ 586	4
+ 643	10
+ 441	0
+ 642	1
+ 628	9
+ 195	6
+ 529	2
+ 551	4
+ 967	6
+ 714	2
+ 383	2
+ 663	2
+ 109	5
+ 955	5
+ 408	8
+ 158	10
+ 223	8
+ 956	7
+ 829	6
+ 717	5
+ 449	9
+ 46	10
+ 104	6
+ 373	1
+ 156	1
+ 226	5
+ 313	9
+ 781	4
+ 426	7
+ 926	8
+ 566	1
+ 830	8
+ 886	8
+ 454	7
+ 384	2
+ 172	8
+ 82	2
+ 811	2
+ 816	2
+ 257	10
+ 272	5
+ 509	6
+ 373	3
+ 6	8
+ 27	9
+ 635	6
+ 16	5
+ 382	9
+ 250	8
+ 617	6
+ 7	8
+ 468	1
+ 7	3
+ 275	8
+ 464	5
+ 794	7
+ 16	3
+ 320	4
+ 593	3
+ 189	6
+ 259	8
+ 213	3
+ 288	6
+ 177	5
+ 431	8
+ 173	4
+ 583	6
+ 527	6
+ 920	8
+ 413	4
+ 335	2
+ 120	4
+ 509	4
+ 740	1
+ 767	9
+ 722	0
+ 754	9
+ 301	0
+ 530	5
+ 581	10
+ 273	8
+ 400	9
+ 395	9
+ 446	3
+ 728	9
+ 700	1
+ 65	8
+ 414	6
+ 260	2
+ 676	0
+ 84	4
+ 52	8
+ 334	4
+ 880	9
+ 832	5
+ 826	1
+ 216	2
+ 960	6
+ 152	4
+ 927	9
+ 265	6
+ 943	3
+ 446	4
+ 904	7
+ 511	6
+ 733	6
+ 979	8
+ 433	3
+ 138	3
+ 177	10
+ 775	0
+ 74	10
+ 227	0
+ 603	4
+ 441	5
+ 259	7
+ 157	2
+ 37	6
+ 559	9
+ 309	1
+ 522	0
+ 666	5
+ 827	1
+ 814	10
+ 413	10
+ 935	2
+ 993	0
+ 180	2
+ 44	8
+ 599	5
+ 312	9
+ 191	5
+ 61	2
+ 73	6
+ 169	4
+ 691	7
+ 424	4
+ 192	3
+ 456	0
+ 217	9
+ 997	2
+ 57	10
+ 162	2
+ 210	2
+ 19	8
+ 690	3
+ 668	9
+ 801	7
+ 109	9
+ 350	3
+ 256	0
+ 969	7
+ 399	2
+ 932	9
+ 168	1
+ 724	2
+ 301	8
+ 154	5
+ 19	4
+ 668	0
+ 173	4
+ 370	8
+ 239	2
+ 570	3
+ 45	9
+ 626	3
+ 962	6
+ 982	4
+ 757	9
+ 216	9
+ 63	9
+ 89	4
+ 722	2
+ 828	7
+ 606	5
+ 779	8
+ 854	1
+ 619	1
+ 320	2
+ 441	4
+ 110	1
+ 666	1
+ 663	6
+ 432	4
+ 562	6
+ 344	6
+ 588	4
+ 989	3
+ 676	8
+ 51	3
+ 313	8
+ 61	2
+ 978	7
+ 260	3
+ 870	7
+ 663	10
+ 768	3
+ 50	4
+ 978	5
+ 850	5
+ 129	2
+ 165	7
+ 628	2
+ 27	3
+ 971	1
+ 586	3
+ 907	6
+ 450	9
+ 327	7
+ 184	2
+ 410	8
+ 175	2
+ 177	2
+ 608	2
+ 707	5
+ 694	8
+ 652	9
+ 554	3
+ 13	6
+ 584	10
+ 658	2
+ 267	6
+ 816	7
+ 450	1
+ 428	6
+ 339	8
+ 480	5
+ 16	7
+ 739	6
+ 811	4
+ 82	5
+ 283	7
+ 364	8
+ 15	4
+ 417	6
+ 360	1
+ 769	6
+ 640	6
+ 345	1
+ 728	8
+ 723	1
+ 611	2
+ 581	6
+ 861	3
+ 252	7
+ 767	3
+ 177	1
+ 69	5
+ 887	1
+ 918	3
+ 684	3
+ 380	5
+ 906	0
+ 38	3
+ 110	8
+ 24	8
+ 833	6
+ 37	4
+ 263	9
+ 733	5
+ 570	5
+ 849	7
+ 550	9
+ 288	4
+ 2	2
+ 742	7
+ 484	1
+ 139	4
+ 142	2
+ 640	3
+ 942	7
+ 85	8
+ 300	1
+ 188	6
+ 20	9
+ 76	6
+ 422	9
+ 336	10
+ 843	6
+ 409	8
+ 830	2
+ 531	3
+ 274	7
+ 704	4
+ 846	3
+ 667	8
+ 8	8
+ 564	3
+ 874	8
+ 870	9
+ 674	9
+ 483	1
+ 871	8
+ 68	7
+ 444	5
+ 559	3
+ 629	1
+ 588	9
+ 760	3
+ 318	6
+ 636	10
+ 395	6
+ 737	10
+ 951	6
+ 711	8
+ 505	4
+ 767	10
+ 480	6
+ 807	5
+ 353	3
+ 25	9
+ 525	7
+ 2	1
+ 555	8
+ 405	9
+ 369	0
+ 858	8
+ 684	6
+ 723	6
+ 207	4
+ 456	7
+ 818	2
+ 701	3
+ 862	5
+ 845	2
+ 759	9
+ 127	3
+ 523	1
+ 397	1
+ 892	8
+ 952	3
+ 841	8
+ 25	5
+ 406	7
+ 160	6
+ 181	6
+ 325	10
+ 840	0
+ 297	7
+ 534	1
+ 916	3
+ 13	0
+ 577	5
+ 172	10
+ 615	1
+ 775	6
+ 325	6
+ 377	3
+ 141	8
+ 97	3
+ 396	3
+ 918	7
+ 278	8
+ 747	6
+ 459	3
+ 717	4
+ 574	7
+ 418	2
+ 265	6
+ 125	9
+ 655	9
+ 447	10
+ 516	8
+ 329	7
+ 606	4
+ 959	0
+ 705	9
+ 723	10
+ 634	5
+ 557	1
+ 751	3
+ 468	3
+ 4	9
+ 477	3
+ 477	6
+ 149	1
+ 501	6
+ 111	0
+ 419	4
+ 674	0
+ 867	6
+ 28	6
+ 509	8
+ 554	1
+ 221	1
+ 236	10
+ 385	7
+ 298	4
+ 590	8
+ 657	1
+ 375	8
+ 200	9
+ 402	3
+ 893	8
+ 752	6
+ 847	6
+ 199	9
+ 190	7
+ 626	7
+ 852	8
+ 854	1
+ 819	2
+ 792	1
+ 627	4
+ 891	3
+ 450	3
+ 91	6
+ 142	5
+ 961	0
+ 315	7
+ 602	2
+ 331	8
+ 37	5
+ 510	7
+ 265	4
+ 509	1
+ 450	3
+ 358	2
+ 445	10
+ 624	3
+ 270	1
+ 602	4
+ 724	7
+ 854	7
+ 779	2
+ 397	4
+ 331	7
+ 182	4
+ 248	7
+ 31	5
+ 55	5
+ 632	5
+ 869	10
+ 746	3
+ 976	4
+ 649	2
+ 445	3
+ 606	2
+ 995	5
+ 853	8
+ 629	2
+ 155	10
+ 977	3
+ 329	2
+ 30	4
+ 738	1
+ 901	4
+ 589	8
+ 361	3
+ 84	3
+ 706	7
+ 582	2
+ 984	2
+ 319	10
+ 648	2
+ 753	3
+ 421	9
+ 237	4
+ 246	6
+ 623	3
+ 926	4
+ 361	8
+ 732	9
+ 597	1
+ 285	7
+ 430	10
+ 414	0
+ 141	4
+ 201	5
+ 378	8
+ 631	1
+ 126	1
+ 40	4
+ 449	3
+ 929	1
+ 562	9
+ 433	9
+ 682	2
+ 872	3
+ 258	2
+ 960	7
+ 147	4
+ 700	3
+ 773	9
+ 747	2
+ 749	4
+ 282	9
+ 429	3
+ 239	9
+ 608	2
+ 949	2
+ 23	4
+ 92	7
+ 546	10
+ 984	8
+ 121	9
+ 491	3
+ 318	2
+ 556	1
+ 91	3
+ 242	8
+ 680	5
+ 716	1
+ 846	10
+ 986	5
+ 123	9
+ 624	1
+ 317	7
+ 851	9
+ 681	8
+ 668	8
+ 778	2
+ 71	1
+ 350	6
+ 186	4
+ 929	4
+ 282	6
+ 952	10
+ 718	8
+ 952	7
+ 252	1
+ 639	9
+ 221	10
+ 593	1
+ 820	3
+ 906	5
+ 76	7
+ 647	1
+ 779	10
+ 774	10
+ 438	7
+ 393	7
+ 312	3
+ 718	0
+ 143	7
+ 734	4
+ 745	4
+ 271	10
+ 330	9
+ 37	1
+ 138	9
+ 637	2
+ 627	3
+ 361	4
+ 281	1
+ 372	7
+ 838	8
+ 440	1
+ 110	2
+ 180	3
+ 828	9
+ 647	6
+ 287	9
+ 538	6
+ 782	6
+ 766	9
+ 518	4
+ 133	1
+ 688	5
+ 551	10
+ 629	9
+ 689	5
+ 688	1
+ 616	8
+ 287	8
+ 50	1
+ 709	7
+ 687	10
+ 616	2
+ 613	4
+ 801	4
+ 316	3
+ 782	4
+ 464	5
+ 944	0
+ 439	6
+ 939	1
+ 39	6
+ 257	7
+ 425	5
+ 451	5
+ 658	2
+ 173	3
+ 157	8
+ 570	8
+ 186	4
+ 148	5
+ 690	9
+ 951	2
+ 400	9
+ 170	8
+ 468	1
+ 967	5
+ 735	2
+ 162	2
+ 768	6
+ 635	4
+ 774	8
+ 771	9
+ 596	3
+ 700	8
+ 712	8
+ 283	4
+ 778	2
+ 556	2
+ 129	7
+ 17	6
+ 834	10
+ 104	6
+ 208	3
+ 729	10
+ 879	4
+ 403	7
+ 171	2
+ 583	8
+ 516	3
+ 548	2
+ 131	8
+ 631	9
+ 66	2
+ 86	2
+ 912	1
+ 792	7
+ 86	9
+ 315	3
+ 161	0
+ 272	0
+ 408	7
+ 693	6
+ 850	3
+ 346	4
+ 559	9
+ 595	7
+ 726	2
+ 598	8
+ 412	7
+ 987	3
+ 786	8
+ 70	9
+ 676	4
+ 167	8
+ 430	4
+ 877	8
+ 113	6
+ 417	10
+ 846	8
+ 330	4
+ 657	9
+ 94	4
+ 150	7
+ 175	6
+ 376	2
+ 886	2
+ 942	10
+ 34	6
+ 342	2
+ 455	8
+ 639	3
+ 610	8
+ 902	0
+ 715	7
+ 789	0
+ 152	4
+ 969	2
+ 829	1
+ 938	0
+ 682	3
+ 166	6
+ 475	1
+ 525	5
+ 725	9
+ 709	2
+ 639	3
+ 511	2
+ 99	4
+ 275	8
+ 160	1
+ 859	3
+ 509	8
+ 558	3
+ 948	5
+ 341	6
+ 809	5
+ 198	3
+ 614	7
+ 792	3
+ 590	5
+ 519	2
+ 848	0
+ 478	9
+ 443	8
+ 761	6
+ 816	6
+ 916	3
+ 448	5
+ 663	4
+ 970	0
+ 26	8
+ 512	2
+ 62	1
+ 947	9
+ 465	5
+ 354	10
+ 766	2
+ 14	2
+ 149	5
+ 996	9
+ 61	8
+ 530	10
+ 138	10
+ 451	8
+ 374	4
+ 806	4
+ 199	3
+ 623	3
+ 443	6
+ 115	9
+ 107	5
+ 893	9
+ 671	9
+ 117	8
+ 365	1
+ 730	4
+ 926	3
+ 403	1
+ 237	9
+ 865	6
+ 275	7
+ 10	5
+ 988	6
+ 736	4
+ 204	9
+ 340	3
+ 321	2
+ 185	10
+ 140	3
+ 812	5
+ 416	5
+ 931	3
+ 802	3
+ 405	0
+ 189	3
+ 650	5
+ 941	7
+ 939	9
+ 295	7
+ 361	5
+ 526	7
+ 810	8
+ 934	10
+ 839	1
+ 297	7
+ 579	7
+ 194	5
+ 54	10
+ 845	5
+ 36	0
+ 730	7
+ 498	7
+ 346	4
+ 602	6
+ 112	10
+ 140	6
+ 665	9
+ 486	6
+ 945	3
+ 673	2
+ 977	3
+ 954	2
+ 763	0
+ 167	6
+ 468	2
+ 641	2
+ 888	1
+ 870	2
+ 576	5
+ 876	7
+ 434	0
+ 326	1
+ 965	8
+ 698	9
+ 136	4
+ 151	1
+ 624	1
+ 284	4
+ 114	5
+ 994	6
+ 654	6
+ 780	5
+ 773	7
+ 777	3
+ 122	7
+ 36	6
+ 669	4
+ 655	6
+ 174	4
+ 544	3
+ 724	7
+ 423	3
+ 801	7
+ 734	9
+ 158	7
+ 497	8
+ 362	3
+ 354	1
+ 928	1
+ 484	0
+ 784	5
+ 605	5
+ 882	3
+ 87	1
+ 613	6
+ 365	3
+ 326	8
+ 685	1
+ 495	4
+ 42	7
+ 148	5
+ 465	5
+ 816	8
+ 646	7
+ 950	1
+ 793	7
+ 649	4
+ 187	5
+ 658	3
+ 587	3
+ 904	10
+ 608	2
+ 740	3
+ 356	2
+ 712	4
+ 888	9
+ 937	4
+ 370	8
+ 172	0
+ 497	1
+ 146	3
+ 855	8
+ 687	0
+ 327	3
+ 315	9
+ 616	2
+ 866	2
+ 449	6
+ 516	8
+ 842	2
+ 203	7
+ 89	1
+ 83	5
+ 893	3
+ 475	4
+ 376	6
+ 679	2
+ 416	4
+ 272	7
+ 711	6
+ 656	3
+ 806	5
+ 550	3
+ 129	1
+ 60	10
+ 295	3
+ 701	4
+ 403	8
+ 843	3
+ 39	3
+ 686	4
+ 940	4
+ 645	4
+ 732	9
+ 99	4
+ 504	8
+ 770	3
+ 278	3
+ 565	4
+ 386	6
+ 377	7
+ 887	1
+ 65	3
+ 861	9
+ 586	9
+ 227	3
+ 315	2
+ 638	10
+ 523	4
+ 878	6
+ 812	4
+ 378	6
+ 693	7
+ 901	3
+ 62	3
+ 881	4
+ 968	8
+ 517	0
+ 58	4
+ 941	6
+ 278	2
+ 917	6
+ 335	6
+ 553	9
+ 923	4
+ 480	7
+ 813	9
+ 316	5
+ 514	2
+ 763	6
+ 504	6
+ 16	5
+ 413	5
+ 505	5
+ 911	4
+ 116	2
+ 614	0
+ 782	9
+ 587	3
+ 806	5
+ 767	3
+ 246	6
+ 145	6
+ 86	7
+ 780	8
+ 236	3
+ 494	3
+ 756	9
+ 785	3
+ 378	7
+ 707	5
+ 885	3
+ 527	7
+ 269	1
+ 4	1
+ 625	8
+ 362	9
+ 351	5
+ 433	4
+ 166	2
+ 287	4
+ 497	8
+ 655	3
+ 688	4
+ 514	1
+ 137	2
+ 561	0
+ 541	1
+ 690	8
+ 202	7
+ 885	8
+ 464	2
+ 698	8
+ 753	1
+ 252	9
+ 345	5
+ 322	8
+ 321	10
+ 95	0
+ 418	6
+ 76	6
+ 829	6
+ 576	4
+ 725	3
+ 180	9
+ 959	1
+ 755	4
+ 311	5
+ 238	1
+ 585	5
+ 983	9
+ 30	3
+ 772	4
+ 283	9
+ 360	7
+ 476	4
+ 256	3
+ 73	8
+ 675	8
+ 98	9
+ 726	1
+ 919	5
+ 480	2
+ 934	7
+ 293	5
+ 208	3
+ 450	2
+ 582	2
+ 588	9
+ 88	9
+ 567	6
+ 384	8
+ 869	5
+ 655	5
+ 255	8
+ 398	10
+ 810	3
+ 461	3
+ 546	4
+ 8	8
+ 915	2
+ 115	4
+ 453	7
+ 586	0
+ 562	7
+ 988	1
+ 238	4
+ 952	1
+ 829	6
+ 650	1
+ 359	0
+ 64	2
+ 365	5
+ 459	9
+ 920	5
+ 749	8
+ 683	9
+ 200	1
+ 561	8
+ 176	1
+ 460	2
+ 252	7
+ 537	2
+ 805	4
+ 810	5
+ 449	2
+ 503	5
+ 338	9
+ 37	8
+ 778	10
+ 264	5
+ 793	9
+ 390	10
+ 83	10
+ 778	3
+ 74	2
+ 424	3
+ 936	10
+ 530	7
+ 326	3
+ 196	8
+ 509	7
+ 287	8
+ 567	3
+ 645	3
+ 282	9
+ 871	1
+ 856	3
+ 68	9
+ 212	8
+ 198	3
+ 84	6
+ 613	0
+ 583	1
+ 761	9
+ 484	10
+ 684	10
+ 657	10
+ 841	2
+ 296	5
+ 569	6
+ 395	4
+ 653	3
+ 702	7
+ 190	9
+ 567	4
+ 201	7
+ 11	8
+ 670	6
+ 957	4
+ 504	4
+ 389	2
+ 435	0
+ 160	3
+ 270	5
+ 761	8
+ 34	2
+ 279	7
+ 407	10
+ 408	6
+ 894	10
+ 986	1
+ 625	10
+ 908	3
+ 592	9
+ 727	1
+ 307	1
+ 285	7
+ 162	4
+ 17	4
+ 900	8
+ 270	9
+ 934	5
+ 622	3
+ 529	0
+ 939	4
+ 5	9
+ 518	6
+ 923	4
+ 925	5
+ 292	7
+ 612	6
+ 768	9
+ 341	9
+ 341	4
+ 362	2
+ 136	6
+ 175	1
+ 181	8
+ 411	7
+ 826	4
+ 134	8
+ 275	7
+ 461	2
+ 79	4
+ 714	4
+ 38	3
+ 970	8
+ 222	3
+ 737	6
+ 668	1
+ 803	8
+ 732	10
+ 873	9
+ 774	3
+ 623	6
+ 635	8
+ 431	9
+ 409	9
+ 108	5
+ 278	8
+ 858	3
+ 147	8
+ 123	4
+ 139	9
+ 931	8
+ 959	7
+ 611	7
+ 712	5
+ 604	5
+ 769	2
+ 86	4
+ 985	5
+ 313	4
+ 408	4
+ 881	7
+ 243	7
+ 2	4
+ 568	1
+ 760	7
+ 985	7
+ 514	9
+ 426	1
+ 636	1
+ 608	2
+ 624	4
+ 467	7
+ 780	5
+ 227	1
+ 846	6
+ 514	7
+ 321	8
+ 467	3
+ 148	0
+ 448	9
+ 742	4
+ 598	3
+ 378	0
+ 380	0
+ 162	10
+ 253	8
+ 365	7
+ 495	1
+ 173	7
+ 238	0
+ 356	8
+ 746	7
+ 510	2
+ 0	7
+ 248	4
+ 565	10
+ 882	2
+ 246	3
+ 187	6
+ 272	3
+ 614	5
+ 134	10
+ 246	6
+ 125	4
+ 350	4
+ 437	7
+ 115	2
+ 384	6
+ 396	4
+ 282	6
+ 833	8
+ 634	7
+ 10	9
+ 973	2
+ 507	2
+ 545	1
+ 771	7
+ 100	0
+ 307	2
+ 435	7
+ 588	9
+ 364	7
+ 55	7
+ 327	5
+ 132	6
+ 95	10
+ 455	7
+ 679	5
+ 609	7
+ 661	1
+ 897	2
+ 237	7
+ 885	3
+ 685	2
+ 563	1
+ 849	2
+ 991	2
+ 853	0
+ 961	2
+ 497	1
+ 788	6
+ 57	2
+ 320	7
+ 708	9
+ 387	4
+ 46	3
+ 575	3
+ 953	5
+ 620	6
+ 652	2
+ 758	5
+ 333	7
+ 714	2
+ 795	7
+ 365	3
+ 767	2
+ 883	8
+ 396	2
+ 559	1
+ 133	9
+ 472	2
+ 232	0
+ 461	2
+ 507	1
+ 823	2
+ 264	6
+ 659	6
+ 329	4
+ 783	1
+ 47	1
+ 416	8
+ 300	3
+ 638	7
+ 502	2
+ 800	6
+ 145	3
+ 814	4
+ 319	3
+ 561	8
+ 356	4
+ 984	6
+ 964	6
+ 218	3
+ 16	0
+ 418	1
+ 148	8
+ 878	4
+ 133	5
+ 144	6
+ 714	9
+ 270	9
+ 217	1
+ 235	5
+ 358	8
+ 362	7
+ 180	3
+ 335	1
+ 989	6
+ 437	0
+ 553	9
+ 69	7
+ 689	9
+ 149	8
+ 463	3
+ 457	2
+ 238	7
+ 36	5
+ 810	3
+ 990	2
+ 67	4
+ 883	2
+ 698	2
+ 390	7
+ 771	8
+ 693	3
+ 683	8
+ 26	4
+ 709	2
+ 194	2
+ 469	7
+ 349	7
+ 377	4
+ 161	2
+ 656	2
+ 355	7
+ 503	2
+ 969	2
+ 456	4
+ 889	2
+ 187	6
+ 553	9
+ 344	6
+ 241	1
+ 754	4
+ 225	2
+ 85	6
+ 929	5
+ 960	1
+ 650	6
+ 241	0
+ 339	7
+ 244	3
+ 946	7
+ 668	8
+ 928	9
+ 418	5
+ 725	8
+ 59	10
+ 815	8
+ 400	0
+ 35	5
+ 614	10
+ 948	6
+ 53	6
+ 190	3
+ 604	5
+ 39	8
+ 838	10
+ 547	5
+ 820	5
+ 361	2
+ 955	1
+ 0	0
+ 52	8
+ 826	5
+ 855	9
+ 938	5
+ 825	9
+ 43	9
+ 484	2
+ 173	1
+ 762	2
+ 935	6
+ 196	5
+ 107	0
+ 957	5
+ 254	9
+ 554	3
+ 926	6
+ 69	8
+ 58	9
+ 614	10
+ 393	4
+ 882	4
+ 317	4
+ 669	5
+ 454	4
+ 701	4
+ 32	9
+ 871	1
+ 913	8
+ 607	2
+ 740	2
+ 421	7
+ 767	5
+ 418	8
+ 414	0
+ 821	8
+ 470	7
+ 244	8
+ 69	9
+ 277	5
+ 345	10
+ 912	4
+ 875	8
+ 516	8
+ 611	1
+ 956	4
+ 285	4
+ 16	1
+ 867	4
+ 878	3
+ 466	7
+ 88	9
+ 401	3
+ 723	5
+ 245	0
+ 992	6
+ 978	9
+ 967	9
+ 687	5
+ 642	3
+ 607	6
+ 648	9
+ 974	7
+ 944	8
+ 98	8
+ 122	6
+ 520	2
+ 500	9
+ 541	2
+ 391	8
+ 223	4
+ 376	2
+ 288	3
+ 55	10
+ 827	7
+ 273	4
+ 294	9
+ 325	3
+ 585	3
+ 108	7
+ 92	2
+ 248	6
+ 440	7
+ 533	10
+ 971	9
+ 767	2
+ 308	1
+ 395	6
+ 487	4
+ 571	3
+ 146	8
+ 747	4
+ 765	1
+ 707	4
+ 342	8
+ 34	4
+ 46	3
+ 46	5
+ 30	6
+ 466	0
+ 504	2
+ 194	8
+ 378	6
+ 408	9
+ 38	10
+ 178	2
+ 823	9
+ 624	6
+ 997	3
+ 939	3
+ 147	10
+ 772	2
+ 255	8
+ 677	3
+ 397	1
+ 286	9
+ 378	5
+ 712	8
+ 69	1
+ 620	1
+ 98	8
+ 291	9
+ 722	9
+ 509	7
+ 245	4
+ 58	4
+ 421	8
+ 584	7
+ 648	3
+ 962	0
+ 405	2
+ 945	8
+ 727	7
+ 538	8
+ 776	2
+ 903	9
+ 956	2
+ 796	7
+ 108	3
+ 397	4
+ 753	5
+ 745	2
+ 285	3
+ 850	9
+ 591	8
+ 977	10
+ 59	9
+ 780	8
+ 578	3
+ 583	4
+ 476	5
+ 228	4
+ 679	0
+ 110	8
+ 329	5
+ 141	1
+ 963	9
+ 255	2
+ 215	1
+ 181	8
+ 917	2
+ 803	10
+ 80	6
+ 763	7
+ 900	3
+ 12	4
+ 831	2
+ 809	5
+ 264	9
+ 297	6
+ 427	4
+ 674	4
+ 324	9
+ 638	5
+ 34	8
+ 346	10
+ 978	1
+ 928	1
+ 730	7
+ 716	6
+ 36	7
+ 7	9
+ 969	8
+ 378	2
+ 735	7
+ 826	2
+ 113	5
+ 552	4
+ 429	2
+ 976	5
+ 10	3
+ 415	10
+ 470	3
+ 46	2
+ 33	8
+ 831	1
+ 490	8
+ 937	5
+ 654	3
+ 691	4
+ 990	5
+ 550	1
+ 17	1
+ 539	4
+ 292	5
+ 909	3
+ 837	3
+ 289	3
+ 666	3
+ 507	7
+ 96	3
+ 769	6
+ 176	7
+ 45	8
+ 21	7
+ 218	0
+ 253	8
+ 113	3
+ 870	7
+ 715	2
+ 167	6
+ 463	0
+ 947	8
+ 312	6
+ 87	8
+ 312	2
+ 157	1
+ 770	3
+ 787	8
+ 163	8
+ 551	4
+ 818	8
+ 150	9
+ 74	0
+ 583	8
+ 182	8
+ 414	6
+ 755	4
+ 397	1
+ 974	5
+ 886	3
+ 668	0
+ 368	4
+ 377	2
+ 253	5
+ 964	8
+ 921	8
+ 609	1
+ 713	7
+ 90	3
+ 472	3
+ 47	9
+ 917	8
+ 247	3
+ 869	2
+ 798	8
+ 508	5
+ 798	9
+ 905	2
+ 32	2
+ 714	10
+ 962	6
+ 777	6
+ 705	5
+ 253	8
+ 787	7
+ 67	8
+ 612	10
+ 636	9
+ 298	5
+ 80	1
+ 259	6
+ 563	1
+ 464	5
+ 232	5
+ 625	9
+ 491	6
+ 581	3
+ 157	3
+ 759	4
+ 82	5
+ 136	1
+ 380	7
+ 132	0
+ 607	4
+ 520	7
+ 526	8
+ 275	1
+ 837	7
+ 556	1
+ 235	2
+ 15	7
+ 768	6
+ 994	9
+ 882	8
+ 336	10
+ 299	5
+ 112	7
+ 220	2
+ 695	8
+ 675	2
+ 513	2
+ 995	8
+ 290	8
+ 527	8
+ 900	8
+ 27	9
+ 488	8
+ 510	5
+ 720	4
+ 235	1
+ 355	5
+ 528	5
+ 213	7
+ 711	9
+ 574	4
+ 122	1
+ 587	1
+ 876	9
+ 948	4
+ 723	8
+ 165	7
+ 764	7
+ 545	3
+ 134	3
+ 666	4
+ 321	0
+ 903	8
+ 489	1
+ 597	2
+ 23	2
+ 586	1
+ 259	2
+ 263	1
+ 50	2
+ 537	8
+ 60	7
+ 522	8
+ 354	1
+ 98	5
+ 331	8
+ 857	7
+ 786	8
+ 501	3
+ 876	1
+ 475	9
+ 269	1
+ 45	5
+ 234	3
+ 662	3
+ 518	2
+ 56	6
+ 900	6
+ 402	3
+ 644	5
+ 743	10
+ 264	6
+ 627	1
+ 360	1
+ 325	2
+ 226	8
+ 134	5
+ 861	2
+ 22	1
+ 486	7
+ 379	0
+ 882	4
+ 582	8
+ 12	10
+ 37	7
+ 484	8
+ 631	7
+ 379	3
+ 799	7
+ 387	1
+ 974	6
+ 925	1
+ 108	8
+ 287	1
+ 881	8
+ 813	3
+ 778	7
+ 695	4
+ 478	7
+ 344	5
+ 364	8
+ 294	10
+ 577	7
+ 254	4
+ 412	6
+ 500	4
+ 254	4
+ 495	4
+ 211	8
+ 491	1
+ 555	3
+ 352	3
+ 1000	0
+ 693	5
+ 755	0
+ 992	1
+ 865	3
+ 114	4
+ 959	4
+ 818	4
+ 9	3
+ 757	3
+ 743	3
+ 625	10
+ 34	1
+ 46	6
+ 421	4
+ 923	4
+ 445	6
+ 898	2
+ 653	9
+ 319	5
+ 175	4
+ 960	1
+ 802	8
+ 505	8
+ 96	3
+ 75	8
+ 515	7
+ 793	5
+ 817	8
+ 139	2
+ 236	1
+ 658	7
+ 677	6
+ 882	3
+ 445	2
+ 848	6
+ 635	8
+ 754	4
+ 586	3
+ 249	7
+ 523	3
+ 522	0
+ 24	3
+ 587	8
+ 153	7
+ 78	4
+ 787	7
+ 71	5
+ 291	10
+ 794	7
+ 155	6
+ 356	8
+ 451	1
+ 228	0
+ 370	5
+ 719	9
+ 801	2
+ 930	8
+ 556	5
+ 667	7
+ 242	7
+ 98	0
+ 481	2
+ 493	8
+ 123	3
+ 508	3
+ 929	9
+ 68	4
+ 974	3
+ 417	3
+ 772	1
+ 237	6
+ 378	2
+ 399	9
+ 683	1
+ 642	9
+ 811	7
+ 954	3
+ 910	4
+ 64	0
+ 734	6
+ 310	7
+ 437	4
+ 43	4
+ 674	5
+ 756	4
+ 596	10
+ 20	10
+ 158	4
+ 907	8
+ 485	5
+ 766	3
+ 290	7
+ 588	2
+ 167	7
+ 233	9
+ 224	5
+ 564	7
+ 922	6
+ 73	6
+ 67	8
+ 41	7
+ 820	1
+ 637	10
+ 480	5
+ 820	10
+ 94	6
+ 260	4
+ 306	8
+ 584	5
+ 500	8
+ 374	7
+ 361	9
+ 385	3
+ 545	5
+ 877	6
+ 286	9
+ 275	1
+ 979	9
+ 85	5
+ 457	9
+ 424	6
+ 492	7
+ 936	8
+ 531	5
+ 271	0
+ 337	6
+ 755	7
+ 583	1
+ 980	1
+ 599	9
+ 739	9
+ 776	0
+ 992	8
+ 926	1
+ 215	4
+ 982	6
+ 935	5
+ 322	9
+ 272	9
+ 391	5
+ 885	7
+ 189	6
+ 426	8
+ 780	4
+ 899	4
+ 264	6
+ 264	0
+ 652	3
+ 796	6
+ 332	0
+ 961	3
+ 649	9
+ 789	10
+ 767	1
+ 825	2
+ 605	7
+ 886	8
+ 349	3
+ 566	1
+ 719	5
+ 508	10
+ 103	8
+ 23	8
+ 28	8
+ 333	4
+ 830	3
+ 675	5
+ 190	5
+ 450	10
+ 525	3
+ 115	1
+ 984	0
+ 924	3
+ 313	5
+ 463	0
+ 955	10
+ 15	1
+ 743	0
+ 813	8
+ 858	1
+ 131	7
+ 440	8
+ 167	6
+ 270	6
+ 587	8
+ 892	7
+ 925	9
+ 702	8
+ 210	0
+ 339	7
+ 47	3
+ 643	1
+ 351	4
+ 101	2
+ 157	10
+ 310	3
+ 647	7
+ 93	8
+ 380	4
+ 432	10
+ 158	3
+ 668	1
+ 201	4
+ 933	4
+ 386	3
+ 83	4
+ 566	7
+ 496	9
+ 113	6
+ 81	3
+ 556	4
+ 557	2
+ 140	7
+ 16	5
+ 13	4
+ 487	2
+ 772	2
+ 253	10
+ 526	2
+ 384	9
+ 458	5
+ 345	0
+ 194	8
+ 941	3
+ 438	0
+ 577	10
+ 413	1
+ 196	6
+ 784	2
+ 74	8
+ 660	6
+ 967	4
+ 716	2
+ 405	2
+ 407	8
+ 154	9
+ 256	5
+ 888	4
+ 341	8
+ 757	8
+ 852	3
+ 771	3
+ 468	10
+ 819	3
+ 179	9
+ 49	8
+ 454	0
+ 271	2
+ 238	7
+ 413	6
+ 465	6
+ 509	7
+ 67	4
+ 171	4
+ 226	9
+ 186	1
+ 261	10
+ 343	7
+ 924	2
+ 982	1
+ 55	0
+ 942	5
+ 48	2
+ 679	3
+ 890	1
+ 930	4
+ 659	4
+ 75	7
+ 836	2
+ 133	1
+ 173	3
+ 141	4
+ 277	5
+ 164	2
+ 646	1
+ 305	7
+ 178	2
+ 210	2
+ 460	9
+ 512	4
+ 981	4
+ 705	6
+ 881	8
+ 366	7
+ 26	5
+ 780	2
+ 818	9
+ 634	1
+ 404	8
+ 296	0
+ 945	6
+ 751	1
+ 848	10
+ 349	3
+ 850	9
+ 658	8
+ 303	4
+ 471	2
+ 143	8
+ 902	2
+ 335	7
+ 368	2
+ 602	0
+ 248	0
+ 800	5
+ 55	7
+ 145	8
+ 868	10
+ 767	2
+ 301	6
+ 78	10
+ 447	4
+ 322	9
+ 566	5
+ 754	5
+ 633	1
+ 149	0
+ 242	8
+ 2	5
+ 757	8
+ 35	8
+ 547	2
+ 618	4
+ 174	4
+ 631	5
+ 1	7
+ 434	4
+ 91	8
+ 366	7
+ 221	1
+ 124	9
+ 208	3
+ 855	5
+ 25	9
+ 941	8
+ 660	10
+ 593	2
+ 157	2
+ 621	3
+ 596	3
+ 806	6
+ 962	2
+ 45	1
+ 996	4
+ 709	2
+ 530	8
+ 72	7
+ 107	9
+ 189	1
+ 784	1
+ 913	4
+ 106	5
+ 650	3
+ 717	3
+ 594	3
+ 524	4
+ 910	5
+ 640	10
+ 538	6
+ 365	2
+ 854	9
+ 80	9
+ 634	2
+ 852	8
+ 318	6
+ 953	2
+ 80	1
+ 737	7
+ 323	5
+ 2	9
+ 766	5
+ 317	7
+ 11	10
+ 630	5
+ 593	10
+ 795	4
+ 891	9
+ 372	5
+ 61	2
+ 348	4
+ 861	3
+ 610	9
+ 360	3
+ 672	7
+ 800	7
+ 599	6
+ 199	9
+ 242	2
+ 873	9
+ 759	5
+ 868	6
+ 912	8
+ 429	3
+ 284	5
+ 507	6
+ 869	4
+ 933	5
+ 309	3
+ 825	10
+ 976	6
+ 654	6
+ 190	9
+ 491	4
+ 63	4
+ 304	8
+ 829	2
+ 377	7
+ 931	8
+ 24	2
+ 295	5
+ 848	2
+ 899	8
+ 642	2
+ 74	5
+ 188	0
+ 92	8
+ 624	3
+ 695	1
+ 714	8
+ 479	0
+ 581	3
+ 191	10
+ 49	1
+ 763	1
+ 337	1
+ 604	2
+ 222	5
+ 965	9
+ 712	0
+ 332	9
+ 88	4
+ 742	7
+ 706	4
+ 828	4
+ 196	3
+ 438	8
+ 616	6
+ 735	7
+ 751	5
+ 738	1
+ 556	3
+ 272	8
+ 846	2
+ 643	6
+ 277	10
+ 457	4
+ 398	2
+ 77	1
+ 636	9
+ 524	8
+ 213	10
+ 609	8
+ 591	3
+ 493	3
+ 842	2
+ 430	4
+ 573	7
+ 177	4
+ 940	8
+ 977	2
+ 795	4
+ 581	2
+ 633	7
+ 297	3
+ 564	8
+ 101	8
+ 783	7
+ 605	4
+ 55	1
+ 716	9
+ 329	1
+ 296	9
+ 847	5
+ 321	8
+ 294	3
+ 3	1
+ 731	6
+ 281	4
+ 243	6
+ 634	8
+ 399	7
+ 583	2
+ 445	2
+ 555	5
+ 286	3
+ 398	6
+ 417	7
+ 517	3
+ 167	8
+ 51	5
+ 135	1
+ 549	9
+ 638	8
+ 231	9
+ 409	9
+ 687	8
+ 599	3
+ 989	0
+ 458	5
+ 545	7
+ 816	9
+ 359	2
+ 637	9
+ 496	8
+ 713	5
+ 265	8
+ 601	8
+ 715	2
+ 645	9
+ 119	1
+ 810	8
+ 862	4
+ 76	9
+ 454	5
+ 395	10
+ 279	2
+ 942	6
+ 442	6
+ 513	9
+ 383	2
+ 486	6
+ 73	1
+ 463	8
+ 325	1
+ 733	4
+ 162	5
+ 251	0
+ 952	3
+ 874	4
+ 862	3
+ 405	1
+ 479	3
+ 778	9
+ 925	3
+ 860	3
+ 516	3
+ 956	6
+ 433	4
+ 377	8
+ 527	1
+ 203	7
+ 654	5
+ 713	6
+ 781	6
+ 12	6
+ 856	4
+ 783	3
+ 763	6
+ 257	7
+ 852	1
+ 995	4
+ 463	10
+ 957	9
+ 369	3
+ 654	9
+ 445	9
+ 584	1
+ 310	3
+ 704	1
+ 884	7
+ 734	7
+ 132	5
+ 75	9
+ 79	3
+ 582	9
+ 449	6
+ 299	9
+ 527	3
+ 808	9
+ 590	5
+ 791	0
+ 318	4
+ 134	6
+ 671	8
+ 721	6
+ 554	5
+ 295	7
+ 973	4
+ 582	1
+ 702	2
+ 983	2
+ 741	3
+ 63	3
+ 538	9
+ 163	1
+ 333	10
+ 164	7
+ 329	3
+ 280	10
+ 136	0
+ 555	7
+ 455	8
+ 377	4
+ 220	10
+ 480	9
+ 122	5
+ 72	9
+ 744	1
+ 130	3
+ 6	3
+ 410	3
+ 248	6
+ 989	6
+ 872	3
+ 577	0
+ 270	1
+ 697	7
+ 981	1
+ 153	2
+ 32	6
+ 122	2
+ 96	2
+ 16	8
+ 329	1
+ 122	3
+ 440	5
+ 673	7
+ 107	7
+ 265	10
+ 932	8
+ 986	2
+ 972	7
+ 927	10
+ 757	1
+ 154	8
+ 713	3
+ 942	8
+ 470	10
+ 649	8
+ 104	8
+ 134	5
+ 305	8
+ 232	4
+ 469	5
+ 390	4
+ 338	4
+ 602	3
+ 58	5
+ 264	8
+ 609	4
+ 603	3
+ 694	5
+ 131	2
+ 503	8
+ 962	6
+ 552	1
+ 152	9
+ 902	4
+ 268	4
+ 881	7
+ 772	2
+ 33	4
+ 529	1
+ 903	8
+ 905	5
+ 210	5
+ 833	9
+ 53	10
+ 67	6
+ 744	0
+ 164	3
+ 125	3
+ 153	0
+ 699	4
+ 398	6
+ 78	2
+ 798	1
+ 544	3
+ 202	4
+ 119	1
+ 959	3
+ 615	8
+ 232	7
+ 756	3
+ 224	5
+ 328	4
+ 797	5
+ 703	10
+ 480	4
+ 371	9
+ 982	4
+ 49	8
+ 561	6
+ 106	8
+ 40	2
+ 869	10
+ 554	5
+ 790	8
+ 151	5
+ 85	4
+ 47	4
+ 763	8
+ 866	5
+ 794	3
+ 868	2
+ 225	8
+ 615	3
+ 629	2
+ 866	7
+ 937	9
+ 960	8
+ 904	5
+ 290	7
+ 301	4
+ 241	4
+ 816	3
+ 799	6
+ 131	7
+ 45	9
+ 12	9
+ 90	2
+ 762	7
+ 510	4
+ 880	4
+ 126	8
+ 282	1
+ 623	2
+ 601	9
+ 880	9
+ 354	1
+ 287	2
+ 408	1
+ 749	5
+ 753	8
+ 464	8
+ 707	6
+ 2	5
+ 258	5
+ 859	1
+ 888	10
+ 956	2
+ 71	6
+ 355	7
+ 492	2
+ 574	8
+ 355	9
+ 15	8
+ 948	8
+ 302	7
+ 558	8
+ 466	3
+ 320	5
+ 733	6
+ 980	6
+ 716	9
+ 577	7
+ 37	6
+ 251	4
+ 321	7
+ 627	9
+ 588	10
+ 756	6
+ 746	7
+ 367	0
+ 405	9
+ 814	9
+ 191	1
+ 338	9
+ 712	3
+ 517	4
+ 186	1
+ 100	2
+ 743	4
+ 615	1
+ 93	2
+ 958	7
+ 225	7
+ 284	10
+ 418	7
+ 19	8
+ 577	8
+ 693	8
+ 967	0
+ 692	7
+ 349	2
+ 106	5
+ 303	2
+ 758	0
+ 557	4
+ 109	7
+ 616	1
+ 332	8
+ 782	6
+ 812	2
+ 267	8
+ 22	8
+ 665	7
+ 612	6
+ 746	3
+ 309	1
+ 512	4
+ 630	8
+ 622	4
+ 860	2
+ 762	10
+ 830	4
+ 37	2
+ 219	8
+ 777	0
+ 19	0
+ 863	0
+ 888	5
+ 756	5
+ 159	5
+ 804	5
+ 597	3
+ 884	2
+ 131	5
+ 616	10
+ 685	4
+ 961	5
+ 756	10
+ 675	10
+ 818	5
+ 6	8
+ 496	9
+ 878	4
+ 397	6
+ 884	6
+ 135	7
+ 23	7
+ 3	9
+ 959	1
+ 412	6
+ 125	1
+ 953	1
+ 611	7
+ 84	3
+ 683	9
+ 739	7
+ 738	2
+ 559	6
+ 619	10
+ 249	5
+ 511	4
+ 190	5
+ 116	2
+ 442	1
+ 327	9
+ 649	5
+ 951	6
+ 538	6
+ 310	6
+ 848	10
+ 524	6
+ 684	3
+ 822	2
+ 878	4
+ 198	1
+ 943	7
+ 512	1
+ 244	6
+ 325	7
+ 702	7
+ 539	4
+ 104	5
+ 952	6
+ 52	3
+ 264	9
+ 257	8
+ 487	9
+ 50	3
+ 183	9
+ 748	4
+ 56	7
+ 91	6
+ 823	3
+ 195	1
+ 21	9
+ 801	6
+ 247	9
+ 50	2
+ 546	1
+ 462	8
+ 2	7
+ 597	5
+ 659	6
+ 797	8
+ 575	5
+ 224	6
+ 236	3
+ 198	1
+ 650	4
+ 208	7
+ 289	0
+ 231	5
+ 913	3
+ 735	5
+ 383	2
+ 268	4
+ 915	9
+ 874	6
+ 512	7
+ 417	1
+ 215	6
+ 718	5
+ 955	9
+ 511	6
+ 309	7
+ 275	6
+ 727	5
+ 133	6
+ 786	9
+ 99	2
+ 64	4
+ 554	10
+ 233	4
+ 554	7
+ 98	10
+ 832	3
+ 611	5
+ 765	6
+ 466	3
+ 170	8
+ 995	4
+ 371	7
+ 951	5
+ 363	7
+ 371	5
+ 907	4
+ 830	5
+ 414	1
+ 889	10
+ 808	10
+ 937	6
+ 301	5
+ 189	1
+ 114	7
+ 343	3
+ 429	3
+ 729	8
+ 61	7
+ 304	4
+ 416	7
+ 886	3
+ 110	7
+ 784	5
+ 779	7
+ 491	6
+ 660	4
+ 226	10
+ 976	4
+ 28	1
+ 71	4
+ 374	5
+ 709	1
+ 300	8
+ 782	6
+ 193	2
+ 280	1
+ 521	4
+ 794	3
+ 913	6
+ 978	4
+ 159	6
+ 833	4
+ 600	8
+ 801	6
+ 899	9
+ 999	3
+ 371	7
+ 376	7
+ 477	2
+ 276	7
+ 356	6
+ 749	9
+ 945	5
+ 183	9
+ 116	2
+ 262	3
+ 799	1
+ 661	4
+ 904	5
+ 28	8
+ 334	0
+ 76	7
+ 735	5
+ 376	2
+ 609	7
+ 882	10
+ 207	6
+ 843	2
+ 174	0
+ 10	3
+ 187	3
+ 565	10
+ 366	2
+ 386	3
+ 689	4
+ 73	0
+ 441	1
+ 727	2
+ 600	1
+ 388	2
+ 756	3
+ 176	10
+ 901	0
+ 115	1
+ 45	1
+ 364	2
+ 396	9
+ 218	8
+ 156	6
+ 32	8
+ 18	1
+ 867	5
+ 254	6
+ 635	9
+ 699	0
+ 65	5
+ 293	2
+ 417	2
+ 259	5
+ 268	3
+ 656	6
+ 535	1
+ 562	8
+ 814	7
+ 357	8
+ 563	4
+ 952	4
+ 834	2
+ 25	5
+ 60	7
+ 492	1
+ 178	8
+ 365	6
+ 977	6
+ 127	2
+ 928	8
+ 877	5
+ 834	4
+ 216	6
+ 157	6
+ 495	7
+ 949	4
+ 150	8
+ 653	2
+ 252	7
+ 898	7
+ 838	1
+ 527	2
+ 671	5
+ 827	8
+ 750	8
+ 581	6
+ 217	4
+ 66	4
+ 64	2
+ 7	6
+ 943	10
+ 6	1
+ 738	7
+ 267	10
+ 372	2
+ 733	2
+ 242	3
+ 413	9
+ 765	2
+ 712	5
+ 994	3
+ 142	2
+ 708	2
+ 645	8
+ 431	7
+ 331	4
+ 608	3
+ 466	3
+ 996	7
+ 336	4
+ 899	1
+ 577	1
+ 330	10
+ 54	1
+ 229	8
+ 609	2
+ 59	8
+ 435	8
+ 959	1
+ 539	4
+ 733	9
+ 763	3
+ 207	2
+ 687	2
+ 961	0
+ 570	9
+ 93	1
+ 1	4
+ 137	1
+ 517	4
+ 821	1
+ 590	9
+ 878	0
+ 646	8
+ 106	2
+ 226	8
+ 56	10
+ 180	3
+ 218	9
+ 466	2
+ 891	0
+ 39	10
+ 183	0
+ 407	3
+ 95	9
+ 686	9
+ 51	3
+ 795	9
+ 301	4
+ 765	4
+ 627	10
+ 246	7
+ 981	4
+ 946	2
+ 294	4
+ 378	2
+ 448	4
+ 170	6
+ 457	6
+ 950	6
+ 501	6
+ 467	6
+ 911	3
+ 480	2
+ 704	2
+ 619	3
+ 237	9
+ 14	2
+ 291	10
+ 416	6
+ 372	8
+ 770	8
+ 212	9
+ 451	7
+ 516	4
+ 221	0
+ 37	7
+ 568	9
+ 950	0
+ 160	7
+ 293	8
+ 985	5
+ 644	10
+ 747	9
+ 960	2
+ 519	3
+ 958	3
+ 151	2
+ 227	6
+ 838	7
+ 3	1
+ 760	0
+ 747	3
+ 988	7
+ 376	1
+ 351	7
+ 928	3
+ 198	6
+ 336	9
+ 506	3
+ 109	0
+ 627	1
+ 312	8
+ 236	5
+ 380	1
+ 283	4
+ 133	0
+ 423	9
+ 371	4
+ 577	7
+ 559	9
+ 415	5
+ 264	6
+ 58	6
+ 559	6
+ 896	7
+ 588	5
+ 734	9
+ 302	10
+ 440	7
+ 45	7
+ 66	2
+ 766	5
+ 58	1
+ 899	6
+ 883	5
+ 562	3
+ 945	8
+ 911	0
+ 427	5
+ 566	3
+ 138	2
+ 847	9
+ 55	1
+ 842	5
+ 832	9
+ 218	9
+ 66	10
+ 386	1
+ 120	3
+ 758	0
+ 743	3
+ 300	7
+ 147	2
+ 690	6
+ 681	3
+ 898	8
+ 411	7
+ 691	5
+ 895	5
+ 960	7
+ 420	2
+ 625	5
+ 162	0
+ 610	3
+ 296	4
+ 283	0
+ 688	6
+ 726	8
+ 794	4
+ 410	5
+ 673	3
+ 294	1
+ 54	10
+ 549	9
+ 518	5
+ 677	9
+ 687	3
+ 425	8
+ 314	0
+ 130	6
+ 402	4
+ 648	1
+ 997	4
+ 926	8
+ 791	3
+ 267	5
+ 645	6
+ 547	7
+ 546	1
+ 649	1
+ 605	3
+ 3	3
+ 628	4
+ 141	9
+ 462	3
+ 551	9
+ 684	2
+ 954	7
+ 574	9
+ 472	4
+ 217	7
+ 828	9
+ 299	4
+ 562	8
+ 471	2
+ 909	1
+ 536	9
+ 367	2
+ 339	5
+ 106	8
+ 779	7
+ 664	5
+ 856	6
+ 144	4
+ 499	6
+ 796	7
+ 353	6
+ 579	7
+ 999	1
+ 497	5
+ 351	4
+ 546	9
+ 317	9
+ 51	7
+ 421	2
+ 456	2
+ 813	1
+ 664	7
+ 738	8
+ 100	2
+ 422	9
+ 953	8
+ 520	5
+ 428	5
+ 672	9
+ 990	0
+ 331	5
+ 910	6
+ 448	10
+ 305	9
+ 118	8
+ 70	9
+ 881	7
+ 601	6
+ 541	7
+ 855	10
+ 597	8
+ 739	1
+ 341	2
+ 637	0
+ 93	6
+ 37	4
+ 162	9
+ 73	6
+ 908	4
+ 480	0
+ 139	6
+ 957	0
+ 284	6
+ 638	8
+ 259	5
+ 788	9
+ 302	5
+ 974	6
+ 695	6
+ 656	8
+ 237	7
+ 212	4
+ 639	3
+ 9	5
+ 663	5
+ 573	8
+ 39	5
+ 821	3
+ 88	5
+ 148	3
+ 952	9
+ 204	3
+ 464	2
+ 896	2
+ 789	6
+ 947	0
+ 244	2
+ 425	9
+ 444	4
+ 430	1
+ 924	0
+ 909	10
+ 533	7
+ 286	6
+ 189	4
+ 969	1
+ 370	2
+ 394	8
+ 350	3
+ 993	1
+ 842	9
+ 165	1
+ 99	6
+ 969	5
+ 24	4
+ 651	9
+ 401	6
+ 911	9
+ 290	2
+ 556	5
+ 631	5
+ 619	0
+ 696	0
+ 835	0
+ 303	8
+ 185	1
+ 767	3
+ 231	9
+ 940	2
+ 410	10
+ 598	1
+ 912	10
+ 621	8
+ 934	9
+ 20	5
+ 389	7
+ 14	0
+ 651	7
+ 22	5
+ 757	3
+ 313	9
+ 471	1
+ 292	7
+ 947	2
+ 902	4
+ 196	5
+ 418	1
+ 500	0
+ 931	4
+ 949	10
+ 924	3
+ 601	9
+ 348	3
+ 648	4
+ 738	4
+ 695	1
+ 347	2
+ 132	6
+ 867	1
+ 872	8
+ 436	1
+ 269	9
+ 176	8
+ 893	1
+ 203	8
+ 59	1
+ 181	7
+ 65	5
+ 912	7
+ 898	7
+ 118	6
+ 702	5
+ 758	8
+ 105	6
+ 913	10
+ 395	3
+ 45	7
+ 204	2
+ 433	1
+ 329	6
+ 939	4
+ 764	1
+ 48	8
+ 650	10
+ 542	5
+ 610	7
+ 141	3
+ 126	9
+ 146	2
+ 525	1
+ 208	9
+ 409	3
+ 584	6
+ 474	0
+ 710	8
+ 654	6
+ 190	4
+ 770	2
+ 247	4
+ 198	8
+ 968	8
+ 448	1
+ 121	6
+ 8	3
+ 805	5
+ 326	0
+ 452	7
+ 265	0
+ 347	7
+ 53	1
+ 542	7
+ 706	7
+ 124	5
+ 970	4
+ 896	2
+ 159	9
+ 977	6
+ 972	1
+ 182	10
+ 364	10
+ 513	7
+ 999	10
+ 425	3
+ 1000	8
+ 3	1
+ 829	5
+ 759	5
+ 277	9
+ 12	2
+ 254	9
+ 415	4
+ 772	4
+ 21	7
+ 490	2
+ 725	9
+ 189	2
+ 544	2
+ 202	10
+ 452	2
+ 741	5
+ 254	6
+ 1000	0
+ 106	3
+ 896	1
+ 523	1
+ 27	9
+ 563	8
+ 330	6
+ 544	8
+ 786	3
+ 674	10
+ 506	2
+ 162	7
+ 186	6
+ 910	9
+ 69	2
+ 496	1
+ 177	6
+ 346	1
+ 720	9
+ 223	7
+ 807	8
+ 546	1
+ 369	1
+ 958	2
+ 358	6
+ 129	9
+ 849	3
+ 573	0
+ 906	5
+ 961	10
+ 646	5
+ 45	8
+ 59	4
+ 896	8
+ 259	1
+ 526	1
+ 904	1
+ 204	3
+ 162	2
+ 428	5
+ 793	6
+ 385	6
+ 849	10
+ 676	8
+ 440	6
+ 731	1
+ 94	8
+ 909	2
+ 166	8
+ 933	4
+ 923	5
+ 492	8
+ 531	7
+ 100	7
+ 858	5
+ 214	7
+ 86	6
+ 292	9
+ 556	10
+ 691	10
+ 604	4
+ 82	7
+ 197	10
+ 851	4
+ 796	8
+ 788	7
+ 243	3
+ 547	8
+ 975	6
+ 467	8
+ 176	7
+ 484	3
+ 279	8
+ 198	8
+ 743	9
+ 832	3
+ 310	9
+ 46	5
+ 906	9
+ 871	7
+ 681	7
+ 422	9
+ 938	10
+ 698	9
+ 615	2
+ 747	8
+ 846	2
+ 53	1
+ 6	3
+ 961	7
+ 139	8
+ 97	4
+ 707	1
+ 957	6
+ 40	8
+ 314	7
+ 487	7
+ 645	4
+ 704	3
+ 339	3
+ 508	1
+ 110	4
+ 315	2
+ 479	3
+ 414	4
+ 70	6
+ 231	2
+ 3	9
+ 311	10
+ 550	4
+ 788	9
+ 72	3
+ 600	7
+ 700	3
+ 60	0
+ 623	6
+ 124	7
+ 922	4
+ 897	4
+ 760	3
+ 839	8
+ 864	1
+ 998	9
+ 9	3
+ 827	6
+ 660	6
+ 423	7
+ 891	0
+ 450	6
+ 327	5
+ 630	10
+ 78	8
+ 685	0
+ 194	6
+ 401	10
+ 893	2
+ 785	8
+ 311	8
+ 625	3
+ 92	5
+ 878	8
+ 68	3
+ 484	10
+ 325	9
+ 550	7
+ 444	2
+ 603	5
+ 935	3
+ 522	1
+ 870	9
+ 82	8
+ 163	9
+ 521	5
+ 650	1
+ 794	7
+ 598	7
+ 494	7
+ 974	10
+ 625	3
+ 911	2
+ 951	4
+ 356	6
+ 877	3
+ 842	4
+ 419	7
+ 322	5
+ 476	5
+ 369	10
+ 960	0
+ 143	8
+ 761	7
+ 426	3
+ 408	4
+ 233	0
+ 698	1
+ 209	6
+ 499	6
+ 203	4
+ 856	0
+ 775	3
+ 757	1
+ 776	2
+ 583	1
+ 229	5
+ 164	4
+ 297	9
+ 114	7
+ 180	5
+ 122	4
+ 555	8
+ 556	8
+ 469	1
+ 328	7
+ 431	2
+ 717	2
+ 459	5
+ 302	2
+ 706	9
+ 380	9
+ 428	5
+ 308	7
+ 468	4
+ 447	6
+ 944	6
+ 60	5
+ 390	6
+ 262	9
+ 672	6
+ 531	1
+ 774	2
+ 307	2
+ 721	6
+ 468	4
+ 495	8
+ 363	9
+ 392	7
+ 648	9
+ 93	1
+ 508	0
+ 664	6
+ 536	1
+ 185	8
+ 913	9
+ 390	4
+ 959	2
+ 692	3
+ 397	4
+ 877	9
+ 841	4
+ 713	2
+ 295	1
+ 875	9
+ 965	10
+ 37	5
+ 6	7
+ 42	5
+ 755	2
+ 342	7
+ 84	7
+ 112	0
+ 895	8
+ 310	3
+ 218	2
+ 158	1
+ 559	9
+ 264	9
+ 976	1
+ 796	9
+ 108	8
+ 413	1
+ 533	5
+ 658	3
+ 682	10
+ 956	8
+ 731	1
+ 809	6
+ 873	1
+ 918	1
+ 307	1
+ 152	9
+ 947	4
+ 719	9
+ 555	5
+ 863	7
+ 347	3
+ 778	9
+ 731	4
+ 168	4
+ 435	1
+ 179	2
+ 193	10
+ 791	1
+ 108	7
+ 159	4
+ 786	3
+ 280	7
+ 726	10
+ 655	3
+ 512	5
+ 944	9
+ 793	7
+ 739	5
+ 158	9
+ 937	6
+ 32	1
+ 758	2
+ 104	5
+ 292	2
+ 259	5
+ 626	0
+ 761	9
+ 777	5
+ 903	4
+ 767	4
+ 949	7
+ 274	7
+ 434	0
+ 266	6
+ 921	2
+ 184	10
+ 318	9
+ 178	4
+ 491	5
+ 633	8
+ 921	3
+ 795	7
+ 164	6
+ 168	1
+ 3	9
+ 483	10
+ 647	8
+ 694	1
+ 771	10
+ 673	7
+ 163	9
+ 644	5
+ 799	8
+ 903	3
+ 292	5
+ 40	2
+ 794	8
+ 895	10
+ 407	1
+ 25	4
+ 999	5
+ 362	6
+ 265	1
+ 727	0
+ 16	4
+ 727	2
+ 257	4
+ 660	1
+ 193	6
+ 345	5
+ 98	4
+ 698	9
+ 221	6
+ 850	6
+ 656	9
+ 37	7
+ 383	4
+ 301	6
+ 455	0
+ 684	5
+ 428	4
+ 650	7
+ 781	3
+ 740	10
+ 872	1
+ 459	10
+ 471	2
+ 863	7
+ 749	7
+ 319	4
+ 589	4
+ 59	10
+ 755	4
+ 621	2
+ 388	3
+ 681	8
+ 716	3
+ 501	5
+ 641	2
+ 471	5
+ 327	9
+ 484	8
+ 87	3
+ 490	8
+ 60	8
+ 241	6
+ 166	3
+ 622	9
+ 661	2
+ 132	0
+ 547	8
+ 865	3
+ 144	4
+ 760	8
+ 606	2
+ 299	9
+ 162	8
+ 731	2
+ 130	2
+ 85	2
+ 30	3
+ 840	2
+ 627	5
+ 117	3
+ 705	2
+ 337	3
+ 61	2
+ 515	2
+ 566	3
+ 991	2
+ 506	3
+ 105	7
+ 74	8
+ 916	2
+ 57	0
+ 395	1
+ 328	2
+ 282	10
+ 697	4
+ 243	4
+ 647	6
+ 654	7
+ 781	2
+ 914	3
+ 444	9
+ 520	9
+ 196	6
+ 618	3
+ 461	5
+ 474	5
+ 535	9
+ 604	9
+ 105	9
+ 818	8
+ 285	1
+ 205	9
+ 641	9
+ 641	4
+ 28	6
+ 768	5
+ 461	3
+ 421	7
+ 913	0
+ 927	4
+ 573	4
+ 892	1
+ 271	5
+ 971	4
+ 382	8
+ 179	7
+ 851	4
+ 600	5
+ 243	2
+ 913	3
+ 796	7
+ 742	3
+ 969	2
+ 914	9
+ 202	8
+ 257	8
+ 243	1
+ 882	5
+ 644	9
+ 891	0
+ 643	1
+ 694	5
+ 454	3
+ 986	7
+ 535	9
+ 967	3
+ 580	7
+ 588	5
+ 871	5
+ 432	1
+ 344	7
+ 847	6
+ 837	7
+ 100	5
+ 583	10
+ 508	2
+ 61	2
+ 721	5
+ 497	7
+ 211	0
+ 606	2
+ 363	2
+ 887	10
+ 736	8
+ 454	2
+ 831	8
+ 858	7
+ 384	7
+ 408	5
+ 176	10
+ 475	7
+ 218	5
+ 887	9
+ 51	4
+ 646	3
+ 415	3
+ 440	8
+ 438	3
+ 729	2
+ 86	2
+ 344	9
+ 981	2
+ 596	4
+ 896	0
+ 850	1
+ 995	3
+ 756	2
+ 861	6
+ 152	9
+ 26	8
+ 174	4
+ 50	6
+ 218	5
+ 942	9
+ 663	0
+ 131	0
+ 944	1
+ 208	5
+ 477	1
+ 544	3
+ 176	5
+ 652	9
+ 752	5
+ 574	9
+ 424	6
+ 702	6
+ 41	8
+ 212	3
+ 241	2
+ 207	9
+ 181	3
+ 911	1
+ 450	1
+ 665	9
+ 222	2
+ 254	4
+ 748	9
+ 329	5
+ 418	9
+ 405	8
+ 504	1
+ 441	5
+ 860	7
+ 803	1
+ 807	0
+ 4	10
+ 348	9
+ 114	8
+ 33	8
+ 725	3
+ 988	10
+ 653	7
+ 885	10
+ 238	3
+ 886	6
+ 146	4
+ 751	6
+ 934	6
+ 240	7
+ 712	0
+ 748	7
+ 35	1
+ 631	1
+ 894	7
+ 928	6
+ 920	9
+ 598	6
+ 654	5
+ 556	9
+ 786	4
+ 535	9
+ 832	3
+ 518	8
+ 896	8
+ 504	6
+ 804	3
+ 324	8
+ 347	10
+ 989	2
+ 619	9
+ 860	5
+ 834	5
+ 113	5
+ 942	7
+ 380	7
+ 112	9
+ 659	9
+ 200	2
+ 711	1
+ 934	2
+ 704	7
+ 466	0
+ 578	8
+ 983	6
+ 55	6
+ 485	9
+ 142	3
+ 373	3
+ 808	3
+ 925	2
+ 43	0
+ 102	7
+ 981	3
+ 878	7
+ 398	8
+ 906	1
+ 551	4
+ 129	1
+ 186	1
+ 697	2
+ 715	2
+ 156	9
+ 502	5
+ 112	3
+ 844	0
+ 497	9
+ 74	6
+ 589	1
+ 900	5
+ 747	3
+ 280	7
+ 399	8
+ 26	5
+ 961	2
+ 641	7
+ 453	4
+ 840	6
+ 212	3
+ 138	3
+ 651	10
+ 362	1
+ 869	4
+ 746	5
+ 490	6
+ 925	2
+ 943	2
+ 890	3
+ 36	9
+ 870	10
+ 128	5
+ 655	6
+ 866	5
+ 190	1
+ 837	3
+ 403	5
+ 310	8
+ 636	2
+ 200	4
+ 637	7
+ 28	6
+ 927	10
+ 766	8
+ 313	8
+ 733	2
+ 798	9
+ 695	5
+ 443	6
+ 948	6
+ 640	8
+ 960	0
+ 274	3
+ 808	9
+ 449	0
+ 292	1
+ 698	3
+ 648	6
+ 291	4
+ 443	6
+ 215	2
+ 788	0
+ 37	5
+ 467	5
+ 44	4
+ 112	7
+ 200	1
+ 727	5
+ 342	5
+ 383	8
+ 542	7
+ 877	2
+ 995	5
+ 866	3
+ 938	3
+ 891	2
+ 484	7
+ 167	5
+ 162	6
+ 1	2
+ 48	1
+ 890	2
+ 186	6
+ 721	5
+ 151	1
+ 318	7
+ 779	2
+ 934	8
+ 719	8
+ 61	7
+ 108	10
+ 810	6
+ 632	10
+ 114	8
+ 610	1
+ 0	7
+ 229	9
+ 906	4
+ 506	6
+ 942	7
+ 731	3
+ 350	5
+ 455	3
+ 284	2
+ 83	3
+ 830	2
+ 297	6
+ 783	9
+ 617	9
+ 723	2
+ 12	7
+ 885	2
+ 614	8
+ 656	1
+ 418	6
+ 777	1
+ 858	1
+ 659	3
+ 411	9
+ 486	5
+ 288	3
+ 685	6
+ 957	5
+ 514	6
+ 365	2
+ 801	4
+ 961	7
+ 618	6
+ 477	3
+ 695	9
+ 871	5
+ 44	7
+ 600	7
+ 42	0
+ 646	5
+ 504	9
+ 845	2
+ 520	8
+ 657	0
+ 375	0
+ 272	2
+ 398	2
+ 862	0
+ 809	3
+ 290	5
+ 234	2
+ 976	3
+ 891	6
+ 982	9
+ 587	6
+ 461	1
+ 563	3
+ 280	1
+ 107	9
+ 117	5
+ 958	4
+ 658	4
+ 623	5
+ 372	4
+ 859	7
+ 935	1
+ 823	9
+ 372	7
+ 488	4
+ 646	1
+ 982	1
+ 165	5
+ 412	4
+ 628	5
+ 382	7
+ 2	3
+ 135	7
+ 696	8
+ 179	1
+ 190	0
+ 730	1
+ 131	6
+ 36	5
+ 266	5
+ 857	9
+ 598	8
+ 19	8
+ 384	4
+ 209	0
+ 951	6
+ 759	10
+ 932	9
+ 612	6
+ 652	8
+ 696	8
+ 830	4
+ 966	10
+ 978	0
+ 464	2
+ 527	3
+ 155	1
+ 160	2
+ 889	5
+ 605	1
+ 556	6
+ 690	3
+ 508	6
+ 209	1
+ 249	9
+ 912	9
+ 703	7
+ 370	7
+ 703	3
+ 672	2
+ 591	2
+ 488	7
+ 324	6
+ 921	2
+ 191	5
+ 311	7
+ 82	0
+ 62	6
+ 621	3
+ 710	9
+ 132	6
+ 815	8
+ 364	2
+ 504	1
+ 533	2
+ 234	1
+ 374	7
+ 872	7
+ 369	8
+ 911	6
+ 319	2
+ 307	4
+ 221	4
+ 990	8
+ 641	7
+ 713	8
+ 323	5
+ 608	7
+ 714	1
+ 755	2
+ 288	10
+ 372	7
+ 711	2
+ 360	1
+ 36	3
+ 640	4
+ 492	9
+ 755	7
+ 317	7
+ 556	10
+ 446	3
+ 731	8
+ 798	3
+ 457	5
+ 450	2
+ 760	7
+ 201	1
+ 400	9
+ 375	8
+ 991	4
+ 31	6
+ 765	5
+ 578	5
+ 237	9
+ 265	8
+ 852	7
+ 63	6
+ 481	9
+ 921	9
+ 374	4
+ 149	1
+ 109	3
+ 265	5
+ 261	6
+ 270	3
+ 50	3
+ 883	8
+ 824	5
+ 335	1
+ 355	6
+ 854	2
+ 312	9
+ 789	8
+ 778	7
+ 730	2
+ 81	9
+ 285	2
+ 228	6
+ 700	5
+ 190	10
+ 740	2
+ 271	6
+ 55	1
+ 84	4
+ 156	4
+ 990	0
+ 646	3
+ 927	4
+ 94	7
+ 145	8
+ 856	2
+ 702	1
+ 417	9
+ 692	1
+ 418	9
+ 87	2
+ 122	4
+ 782	2
+ 453	9
+ 567	6
+ 305	6
+ 619	10
+ 859	5
+ 386	10
+ 250	5
+ 776	1
+ 757	5
+ 249	2
+ 407	9
+ 291	8
+ 823	4
+ 983	9
+ 736	8
+ 121	2
+ 631	7
+ 798	9
+ 245	4
+ 886	1
+ 963	3
+ 57	2
+ 803	8
+ 320	6
+ 310	6
+ 734	7
+ 509	0
+ 543	3
+ 403	5
+ 276	1
+ 291	4
+ 328	9
+ 85	1
+ 858	3
+ 544	7
+ 434	5
+ 16	5
+ 719	8
+ 324	0
+ 378	6
+ 607	1
+ 352	1
+ 137	9
+ 447	5
+ 420	7
+ 679	7
+ 119	0
+ 634	2
+ 134	5
+ 535	7
+ 236	10
+ 184	3
+ 461	9
+ 71	8
+ 942	4
+ 418	5
+ 561	8
+ 664	7
+ 664	1
+ 238	1
+ 834	9
+ 796	10
+ 924	4
+ 158	1
+ 921	7
+ 735	2
+ 662	9
+ 409	1
+ 822	5
+ 907	8
+ 929	3
+ 312	5
+ 95	10
+ 188	8
+ 87	4
+ 844	9
+ 342	6
+ 874	3
+ 69	0
+ 324	10
+ 724	1
+ 148	4
+ 977	6
+ 510	8
+ 38	4
+ 563	10
+ 743	9
+ 458	8
+ 851	6
+ 598	9
+ 72	4
+ 859	4
+ 81	7
+ 680	2
+ 764	0
+ 141	5
+ 63	3
+ 875	0
+ 846	4
+ 839	9
+ 801	4
+ 851	5
+ 277	3
+ 382	1
+ 955	10
+ 65	0
+ 421	9
+ 441	5
+ 656	1
+ 653	4
+ 125	8
+ 908	2
+ 83	8
+ 228	9
+ 167	1
+ 813	10
+ 469	7
+ 513	7
+ 974	9
+ 873	9
+ 875	9
+ 955	3
+ 862	4
+ 799	5
+ 517	5
+ 939	6
+ 245	8
+ 830	3
+ 630	1
+ 257	8
+ 126	1
+ 765	6
+ 734	3
+ 341	7
+ 173	2
+ 637	0
+ 152	6
+ 344	0
+ 988	1
+ 533	5
+ 594	5
+ 148	8
+ 319	10
+ 166	9
+ 37	4
+ 745	2
+ 493	5
+ 757	2
+ 788	1
+ 935	10
+ 311	6
+ 9	5
+ 164	4
+ 478	2
+ 495	0
+ 658	1
+ 483	8
+ 927	8
+ 785	1
+ 751	8
+ 516	5
+ 984	0
+ 7	7
+ 235	8
+ 840	2
+ 756	2
+ 742	8
+ 615	9
+ 118	1
+ 58	6
+ 104	7
+ 700	6
+ 522	6
+ 389	3
+ 720	1
+ 128	2
+ 637	1
+ 244	6
+ 854	5
+ 439	7
+ 650	2
+ 845	4
+ 961	5
+ 298	1
+ 552	4
+ 690	7
+ 72	4
+ 243	6
+ 18	6
+ 901	7
+ 772	0
+ 973	4
+ 142	2
+ 52	10
+ 695	5
+ 691	3
+ 687	5
+ 737	6
+ 995	0
+ 725	5
+ 392	4
+ 203	5
+ 806	4
+ 59	8
+ 77	10
+ 562	8
+ 989	5
+ 258	1
+ 751	3
+ 127	4
+ 802	8
+ 792	5
+ 353	5
+ 136	3
+ 564	9
+ 895	10
+ 278	1
+ 420	1
+ 544	5
+ 908	6
+ 438	5
+ 471	4
+ 5	7
+ 558	8
+ 40	7
+ 203	8
+ 503	10
+ 331	9
+ 523	5
+ 205	1
+ 330	1
+ 42	6
+ 199	5
+ 692	7
+ 941	6
+ 363	4
+ 70	8
+ 806	1
+ 563	4
+ 831	6
+ 49	0
+ 445	6
+ 28	8
+ 408	6
+ 245	6
+ 638	6
+ 713	7
+ 182	9
+ 142	9
+ 654	1
+ 473	0
+ 463	5
+ 852	3
+ 619	4
+ 632	4
+ 18	7
+ 484	5
+ 233	5
+ 240	6
+ 63	5
+ 254	7
+ 59	10
+ 380	2
+ 880	5
+ 114	5
+ 606	6
+ 551	1
+ 131	4
+ 337	7
+ 818	10
+ 199	8
+ 650	7
+ 299	9
+ 195	5
+ 524	3
+ 23	8
+ 958	1
+ 746	3
+ 322	6
+ 860	4
+ 159	5
+ 23	7
+ 535	2
+ 114	9
+ 904	9
+ 842	1
+ 767	5
+ 786	1
+ 375	10
+ 604	9
+ 239	6
+ 678	2
+ 708	4
+ 534	0
+ 49	4
+ 466	2
+ 861	5
+ 919	4
+ 643	0
+ 269	5
+ 964	1
+ 650	7
+ 603	4
+ 797	10
+ 418	4
+ 877	7
+ 28	6
+ 853	7
+ 979	4
+ 766	0
+ 782	2
+ 236	6
+ 721	2
+ 40	4
+ 188	3
+ 911	2
+ 419	6
+ 884	0
+ 999	7
+ 1000	4
+ 82	9
+ 73	1
+ 432	9
+ 846	4
+ 313	6
+ 439	1
+ 844	7
+ 739	6
+ 831	8
+ 929	0
+ 87	8
+ 172	5
+ 402	1
+ 528	4
+ 736	5
+ 817	8
+ 405	9
+ 927	8
+ 817	8
+ 249	1
+ 384	7
+ 225	2
+ 364	10
+ 793	2
+ 742	7
+ 215	8
+ 562	4
+ 336	10
+ 443	9
+ 365	2
+ 392	2
+ 997	8
+ 72	9
+ 635	9
+ 697	9
+ 19	1
+ 573	2
+ 309	9
+ 208	1
+ 132	10
+ 824	3
+ 780	4
+ 734	1
+ 351	2
+ 980	7
+ 356	4
+ 897	4
+ 170	10
+ 276	8
+ 858	10
+ 690	9
+ 54	3
+ 121	4
+ 199	3
+ 466	3
+ 280	3
+ 678	1
+ 677	4
+ 175	0
+ 589	2
+ 743	9
+ 527	6
+ 297	7
+ 610	6
+ 502	5
+ 547	2
+ 345	6
+ 454	5
+ 965	7
+ 795	4
+ 983	1
+ 721	7
+ 135	4
+ 74	3
+ 425	7
+ 465	2
+ 607	10
+ 808	9
+ 689	4
+ 478	2
+ 886	0
+ 382	2
+ 626	8
+ 697	6
+ 488	5
+ 21	5
+ 567	7
+ 133	7
+ 140	2
+ 12	6
+ 869	5
+ 734	5
+ 469	5
+ 381	2
+ 960	9
+ 349	8
+ 884	7
+ 77	5
+ 567	8
+ 100	1
+ 266	1
+ 527	8
+ 864	7
+ 535	0
+ 867	5
+ 570	7
+ 24	3
+ 213	5
+ 845	6
+ 651	8
+ 453	0
+ 651	3
+ 732	7
+ 846	3
+ 501	9
+ 355	8
+ 67	9
+ 600	9
+ 542	1
+ 935	4
+ 682	5
+ 146	7
+ 808	4
+ 199	7
+ 953	9
+ 459	4
+ 851	1
+ 743	6
+ 837	6
+ 882	3
+ 534	2
+ 105	6
+ 118	7
+ 532	7
+ 840	5
+ 70	5
+ 971	2
+ 228	8
+ 575	4
+ 433	5
+ 277	9
+ 935	1
+ 1	7
+ 710	8
+ 266	6
+ 176	8
+ 828	3
+ 402	9
+ 986	9
+ 607	8
+ 399	7
+ 348	4
+ 892	6
+ 150	5
+ 1	6
+ 996	3
+ 474	9
+ 406	5
+ 609	1
+ 312	9
+ 708	5
+ 676	5
+ 768	1
+ 483	8
+ 10	1
+ 580	4
+ 766	9
+ 780	7
+ 502	9
+ 125	5
+ 513	1
+ 782	10
+ 52	2
+ 461	7
+ 304	8
+ 535	0
+ 261	2
+ 548	0
+ 288	0
+ 783	3
+ 121	4
+ 708	9
+ 290	5
+ 545	8
+ 418	7
+ 296	9
+ 791	1
+ 918	8
+ 266	4
+ 504	6
+ 153	0
+ 581	4
+ 250	1
+ 443	5
+ 161	2
+ 836	3
+ 589	5
+ 169	9
+ 32	7
+ 671	4
+ 384	10
+ 381	2
+ 45	3
+ 19	3
+ 678	5
+ 881	8
+ 561	5
+ 244	8
+ 591	7
+ 349	8
+ 913	2
+ 34	5
+ 728	2
+ 380	8
+ 916	1
+ 209	3
+ 18	6
+ 476	1
+ 890	5
+ 374	6
+ 17	3
+ 399	6
+ 716	6
+ 389	3
+ 330	7
+ 60	2
+ 922	1
+ 744	6
+ 296	1
+ 409	2
+ 174	6
+ 513	2
+ 209	10
+ 255	1
+ 483	6
+ 667	5
+ 883	1
+ 78	6
+ 708	5
+ 907	0
+ 204	10
+ 280	1
+ 60	0
+ 775	4
+ 147	2
+ 569	3
+ 803	1
+ 512	0
+ 71	8
+ 111	6
+ 396	8
+ 53	3
+ 842	1
+ 878	6
+ 597	8
+ 588	8
+ 751	9
+ 927	8
+ 891	7
+ 169	0
+ 886	7
+ 359	7
+ 820	9
+ 701	9
+ 638	8
+ 445	0
+ 588	5
+ 312	4
+ 628	2
+ 981	2
+ 975	6
+ 26	7
+ 437	10
+ 538	3
+ 655	7
+ 366	5
+ 445	7
+ 229	3
+ 595	9
+ 156	2
+ 741	6
+ 266	3
+ 98	6
+ 760	7
+ 768	7
+ 952	7
+ 310	10
+ 469	7
+ 931	0
+ 74	6
+ 713	4
+ 127	2
+ 164	4
+ 423	8
+ 286	6
+ 992	0
+ 180	3
+ 357	3
+ 837	1
+ 5	6
+ 858	10
+ 348	2
+ 935	8
+ 915	9
+ 823	10
+ 452	5
+ 429	6
+ 694	6
+ 935	1
+ 352	2
+ 696	3
+ 249	9
+ 602	6
+ 153	4
+ 722	2
+ 44	6
+ 115	4
+ 748	0
+ 208	7
+ 915	0
+ 652	4
+ 566	1
+ 946	3
+ 673	9
+ 377	0
+ 102	1
+ 369	4
+ 948	10
+ 956	1
+ 409	7
+ 259	5
+ 258	4
+ 844	0
+ 423	1
+ 669	3
+ 82	3
+ 705	6
+ 402	7
+ 908	1
+ 533	3
+ 101	6
+ 357	5
+ 986	3
+ 440	9
+ 406	8
+ 620	7
+ 303	9
+ 39	1
+ 885	5
+ 199	6
+ 801	3
+ 875	5
+ 929	3
+ 157	8
+ 353	7
+ 123	5
+ 325	5
+ 923	3
+ 785	4
+ 252	2
+ 213	9
+ 857	5
+ 751	9
+ 663	6
+ 359	9
+ 190	2
+ 142	1
+ 665	1
+ 343	8
+ 909	7
+ 513	0
+ 149	8
+ 513	1
+ 148	3
+ 435	4
+ 489	6
+ 273	3
+ 163	0
+ 243	8
+ 660	6
+ 687	8
+ 761	8
+ 914	4
+ 901	3
+ 249	8
+ 952	8
+ 843	1
+ 600	4
+ 173	7
+ 653	6
+ 149	1
+ 255	4
+ 489	4
+ 446	7
+ 244	1
+ 334	9
+ 955	1
+ 760	9
+ 521	7
+ 126	8
+ 471	1
+ 532	3
+ 180	1
+ 668	4
+ 880	3
+ 961	0
+ 464	2
+ 450	10
+ 634	9
+ 685	9
+ 2	0
+ 809	10
+ 113	6
+ 826	6
+ 230	10
+ 405	7
+ 30	9
+ 14	2
+ 69	7
+ 563	9
+ 3	5
+ 978	5
+ 740	4
+ 420	4
+ 324	1
+ 252	3
+ 123	1
+ 283	2
+ 631	1
+ 871	9
+ 60	3
+ 561	1
+ 213	6
+ 301	3
+ 257	9
+ 232	3
+ 388	2
+ 727	1
+ 637	1
+ 501	10
+ 252	8
+ 288	4
+ 815	6
+ 612	4
+ 678	5
+ 306	7
+ 759	9
+ 829	10
+ 442	1
+ 255	7
+ 994	5
+ 959	4
+ 696	7
+ 509	3
+ 833	0
+ 294	1
+ 764	6
+ 461	6
+ 152	1
+ 25	8
+ 555	3
+ 569	3
+ 199	4
+ 287	6
+ 528	5
+ 339	5
+ 28	3
+ 903	7
+ 983	4
+ 57	8
+ 422	4
+ 902	2
+ 933	4
+ 765	1
+ 435	8
+ 915	10
+ 122	5
+ 304	3
+ 882	6
+ 961	4
+ 133	3
+ 931	2
+ 598	8
+ 885	6
+ 246	9
+ 397	7
+ 292	3
+ 853	2
+ 662	6
+ 310	1
+ 409	2
+ 86	5
+ 709	4
+ 852	6
+ 982	8
+ 1	1
+ 114	9
+ 276	7
+ 766	2
+ 293	0
+ 102	7
+ 680	4
+ 989	5
+ 620	7
+ 152	9
+ 747	6
+ 154	8
+ 92	9
+ 224	9
+ 454	2
+ 758	5
+ 321	9
+ 386	6
+ 584	2
+ 758	9
+ 164	9
+ 567	8
+ 255	6
+ 377	9
+ 207	5
+ 804	10
+ 89	10
+ 788	2
+ 821	0
+ 126	3
+ 218	9
+ 729	5
+ 757	1
+ 136	3
+ 267	9
+ 219	4
+ 755	8
+ 275	0
+ 342	7
+ 885	5
+ 179	7
+ 503	3
+ 648	3
+ 450	5
+ 303	6
+ 743	5
+ 460	5
+ 60	2
+ 587	2
+ 559	9
+ 91	8
+ 285	8
+ 563	6
+ 856	9
+ 211	7
+ 454	4
+ 430	10
+ 659	1
+ 249	1
+ 546	6
+ 685	3
+ 72	1
+ 762	1
+ 363	3
+ 328	9
+ 202	4
+ 699	5
+ 265	3
+ 47	1
+ 168	3
+ 862	6
+ 649	3
+ 580	3
+ 369	8
+ 417	9
+ 379	1
+ 205	5
+ 247	10
+ 583	6
+ 315	9
+ 532	5
+ 331	2
+ 5	6
+ 493	1
+ 717	7
+ 310	6
+ 283	10
+ 870	9
+ 267	2
+ 691	7
+ 154	1
+ 786	4
+ 522	0
+ 326	1
+ 642	6
+ 17	2
+ 158	3
+ 405	2
+ 943	9
+ 215	7
+ 559	5
+ 238	8
+ 484	1
+ 704	8
+ 346	4
+ 435	5
+ 465	2
+ 860	10
+ 253	2
+ 92	9
+ 826	1
+ 70	10
+ 456	5
+ 147	4
+ 373	4
+ 60	9
+ 887	3
+ 774	4
+ 405	5
+ 122	8
+ 873	6
+ 253	3
+ 778	1
+ 326	0
+ 298	4
+ 927	1
+ 527	10
+ 109	10
+ 471	3
+ 383	8
+ 618	4
+ 775	5
+ 740	5
+ 875	1
+ 27	10
+ 897	9
+ 554	1
+ 239	3
+ 263	6
+ 362	6
+ 982	3
+ 686	5
+ 285	8
+ 492	8
+ 51	9
+ 600	7
+ 317	4
+ 173	1
+ 924	0
+ 203	10
+ 45	1
+ 851	6
+ 250	1
+ 930	5
+ 654	3
+ 74	6
+ 581	8
+ 145	9
+ 554	6
+ 623	6
+ 511	2
+ 274	8
+ 598	4
+ 886	5
+ 496	1
+ 474	5
+ 189	3
+ 141	4
+ 414	1
+ 953	1
+ 363	0
+ 704	9
+ 786	8
+ 811	3
+ 485	4
+ 946	10
+ 657	2
+ 825	3
+ 668	7
+ 778	2
+ 800	3
+ 705	10
+ 576	8
+ 429	10
+ 916	4
+ 58	3
+ 409	8
+ 225	2
+ 610	0
+ 536	1
+ 470	5
+ 92	1
+ 702	9
+ 383	4
+ 628	2
+ 533	4
+ 412	2
+ 417	10
+ 84	8
+ 978	0
+ 229	0
+ 280	6
+ 798	5
+ 834	4
+ 540	4
+ 504	0
+ 852	6
+ 138	6
+ 512	5
+ 925	1
+ 682	5
+ 567	1
+ 696	10
+ 82	8
+ 830	1
+ 780	1
+ 96	1
+ 697	9
+ 565	5
+ 302	1
+ 900	8
+ 116	8
+ 401	3
+ 307	9
+ 774	2
+ 52	5
+ 690	6
+ 550	4
+ 603	6
+ 166	4
+ 691	9
+ 493	8
+ 7	2
+ 681	6
+ 720	10
+ 677	6
+ 789	8
+ 374	2
+ 46	7
+ 103	8
+ 913	2
+ 276	6
+ 774	8
+ 989	4
+ 457	2
+ 811	1
+ 102	3
+ 935	1
+ 493	6
+ 680	2
+ 601	4
+ 835	4
+ 149	2
+ 580	2
+ 889	7
+ 14	8
+ 838	3
+ 404	6
+ 115	4
+ 989	6
+ 548	8
+ 720	6
+ 103	7
+ 758	6
+ 272	4
+ 810	9
+ 795	6
+ 262	9
+ 852	8
+ 138	7
+ 525	2
+ 543	4
+ 442	9
+ 975	6
+ 340	10
+ 129	9
+ 764	8
+ 537	9
+ 504	3
+ 463	8
+ 733	3
+ 649	5
+ 916	9
+ 471	8
+ 754	6
+ 510	3
+ 761	1
+ 642	2
+ 1000	6
+ 761	3
+ 581	9
+ 227	3
+ 740	8
+ 211	2
+ 58	7
+ 21	8
+ 946	7
+ 318	9
+ 582	8
+ 631	3
+ 398	1
+ 615	2
+ 194	3
+ 363	2
+ 875	1
+ 533	5
+ 16	8
+ 801	8
+ 522	0
+ 0	6
+ 686	0
+ 371	6
+ 692	7
+ 494	3
+ 478	1
+ 610	9
+ 266	2
+ 36	5
+ 483	4
+ 653	4
+ 524	2
+ 814	5
+ 945	6
+ 296	5
+ 627	3
+ 47	3
+ 318	4
+ 944	0
+ 108	4
+ 283	6
+ 564	9
+ 463	8
+ 118	5
+ 290	6
+ 898	9
+ 959	4
+ 129	8
+ 963	1
+ 388	3
+ 541	0
+ 555	6
+ 327	9
+ 6	3
+ 882	1
+ 711	2
+ 700	3
+ 58	2
+ 105	2
+ 662	4
+ 777	6
+ 338	7
+ 983	5
+ 509	9
+ 540	9
+ 205	1
+ 912	8
+ 669	2
+ 633	7
+ 511	5
+ 790	2
+ 680	5
+ 496	7
+ 653	6
+ 915	3
+ 995	7
+ 875	3
+ 429	9
+ 800	9
+ 804	3
+ 835	0
+ 422	7
+ 768	1
+ 987	4
+ 767	5
+ 915	6
+ 720	6
+ 47	2
+ 334	7
+ 817	2
+ 15	8
+ 941	9
+ 145	4
+ 747	9
+ 307	6
+ 286	1
+ 559	7
+ 890	3
+ 798	9
+ 727	6
+ 375	6
+ 122	1
+ 238	2
+ 311	6
+ 869	1
+ 820	9
+ 941	8
+ 773	1
+ 130	5
+ 31	4
+ 70	3
+ 580	6
+ 24	5
+ 956	8
+ 347	7
+ 387	7
+ 325	5
+ 817	6
+ 678	1
+ 134	5
+ 257	10
+ 431	2
+ 715	2
+ 284	8
+ 724	3
+ 281	8
+ 632	9
+ 423	7
+ 331	4
+ 477	7
+ 62	9
+ 400	4
+ 374	2
+ 950	1
+ 346	1
+ 599	6
+ 38	0
+ 800	8
+ 234	1
+ 597	10
+ 399	9
+ 751	0
+ 740	2
+ 686	1
+ 554	2
+ 749	6
+ 28	1
+ 2	4
+ 366	10
+ 454	7
+ 36	1
+ 314	1
+ 83	1
+ 827	3
+ 198	4
+ 275	6
+ 304	0
+ 628	0
+ 201	3
+ 114	8
+ 477	9
+ 370	5
+ 12	4
+ 907	4
+ 324	4
+ 89	4
+ 414	4
+ 435	5
+ 517	3
+ 815	7
+ 687	1
+ 313	10
+ 116	9
+ 34	3
+ 255	1
+ 71	7
+ 12	4
+ 237	0
+ 812	1
+ 401	1
+ 505	5
+ 496	9
+ 893	9
+ 417	4
+ 193	2
+ 125	9
+ 321	4
+ 871	4
+ 380	9
+ 753	6
+ 53	8
+ 366	1
+ 264	6
+ 88	1
+ 747	5
+ 213	3
+ 979	7
+ 171	9
+ 640	6
+ 281	8
+ 819	4
+ 714	1
+ 845	6
+ 577	2
+ 489	3
+ 859	5
+ 154	2
+ 607	4
+ 828	7
+ 495	6
+ 184	7
+ 827	2
+ 417	10
+ 34	1
+ 586	3
+ 890	4
+ 721	6
+ 545	6
+ 188	1
+ 791	7
+ 452	7
+ 219	6
+ 875	8
+ 25	7
+ 521	5
+ 279	7
+ 228	1
+ 868	6
+ 105	9
+ 701	7
+ 217	6
+ 96	9
+ 196	6
+ 505	4
+ 763	3
+ 61	2
+ 946	3
+ 823	8
+ 107	8
+ 525	6
+ 368	8
+ 333	6
+ 910	2
+ 240	0
+ 103	9
+ 706	3
+ 533	8
+ 258	7
+ 443	8
+ 112	2
+ 58	2
+ 423	0
+ 455	2
+ 825	6
+ 93	4
+ 190	5
+ 154	5
+ 56	1
+ 725	3
+ 79	8
+ 237	8
+ 147	8
+ 587	4
+ 498	0
+ 167	6
+ 236	2
+ 785	7
+ 230	2
+ 904	1
+ 801	10
+ 405	10
+ 458	6
+ 515	5
+ 623	2
+ 810	7
+ 67	0
+ 486	2
+ 817	1
+ 619	3
+ 102	8
+ 926	3
+ 11	7
+ 998	2
+ 950	9
+ 297	8
+ 899	7
+ 743	4
+ 261	3
+ 871	9
+ 498	7
+ 585	6
+ 728	1
+ 779	5
+ 144	4
+ 861	2
+ 183	8
+ 585	2
+ 498	6
+ 436	4
+ 485	7
+ 200	4
+ 434	9
+ 741	7
+ 202	6
+ 578	7
+ 293	2
+ 264	0
+ 234	0
+ 566	4
+ 440	4
+ 624	6
+ 213	2
+ 817	7
+ 791	3
+ 160	3
+ 984	4
+ 660	4
+ 303	4
+ 113	5
+ 13	7
+ 204	3
+ 855	5
+ 326	1
+ 511	9
+ 467	10
+ 318	1
+ 573	5
+ 300	4
+ 242	1
+ 642	4
+ 367	6
+ 762	0
+ 45	1
+ 428	2
+ 570	4
+ 851	8
+ 746	7
+ 243	1
+ 795	8
+ 963	3
+ 705	3
+ 354	3
+ 811	7
+ 668	1
+ 744	3
+ 456	1
+ 937	2
+ 137	10
+ 283	6
+ 140	9
+ 5	10
+ 628	8
+ 697	9
+ 823	5
+ 626	8
+ 755	3
+ 66	1
+ 609	9
+ 762	3
+ 931	5
+ 586	4
+ 616	5
+ 604	8
+ 505	9
+ 318	6
+ 741	3
+ 636	4
+ 74	3
+ 241	9
+ 825	9
+ 683	6
+ 197	7
+ 688	8
+ 626	5
+ 82	6
+ 956	7
+ 944	6
+ 192	5
+ 325	7
+ 436	6
+ 342	2
+ 965	10
+ 547	0
+ 312	8
+ 936	1
+ 654	6
+ 717	9
+ 368	4
+ 658	10
+ 855	7
+ 551	8
+ 409	5
+ 382	6
+ 44	7
+ 298	5
+ 349	6
+ 658	3
+ 619	2
+ 354	9
+ 992	3
+ 67	6
+ 909	8
+ 498	3
+ 188	2
+ 271	0
+ 895	8
+ 854	3
+ 318	2
+ 905	4
+ 943	2
+ 843	3
+ 843	5
+ 607	5
+ 705	10
+ 392	7
+ 251	5
+ 343	2
+ 242	8
+ 437	4
+ 995	7
+ 474	9
+ 530	3
+ 195	8
+ 565	1
+ 210	5
+ 303	1
+ 800	1
+ 553	4
+ 609	3
+ 368	0
+ 955	6
+ 460	3
+ 780	7
+ 138	2
+ 133	1
+ 926	6
+ 24	5
+ 935	2
+ 304	5
+ 318	5
+ 8	6
+ 568	8
+ 768	1
+ 216	4
+ 379	6
+ 377	3
+ 204	8
+ 631	10
+ 539	8
+ 202	7
+ 901	1
+ 279	9
+ 584	2
+ 143	9
+ 714	5
+ 403	7
+ 83	10
+ 530	9
+ 92	7
+ 228	5
+ 331	6
+ 804	5
+ 442	4
+ 520	10
+ 203	7
+ 652	1
+ 850	9
+ 29	4
+ 145	2
+ 323	9
+ 633	7
+ 581	7
+ 696	1
+ 567	8
+ 857	8
+ 259	2
+ 400	1
+ 723	8
+ 498	2
+ 822	7
+ 965	5
+ 804	8
+ 405	8
+ 249	6
+ 5	6
+ 409	6
+ 297	10
+ 354	10
+ 100	9
+ 782	10
+ 716	0
+ 146	1
+ 104	9
+ 958	6
+ 112	8
+ 302	1
+ 255	1
+ 892	7
+ 939	1
+ 211	9
+ 713	6
+ 582	0
+ 609	9
+ 4	7
+ 857	8
+ 667	6
+ 828	8
+ 690	9
+ 683	6
+ 533	8
+ 428	8
+ 873	7
+ 942	8
+ 344	9
+ 907	6
+ 825	6
+ 174	4
+ 630	8
+ 343	6
+ 492	2
+ 421	2
+ 774	2
+ 972	5
+ 180	7
+ 112	7
+ 450	5
+ 549	3
+ 224	5
+ 88	6
+ 372	10
+ 122	2
+ 614	3
+ 604	2
+ 77	9
+ 880	6
+ 148	3
+ 728	9
+ 550	7
+ 386	7
+ 354	5
+ 444	8
+ 38	10
+ 127	3
+ 484	2
+ 829	9
+ 209	10
+ 53	8
+ 245	7
+ 68	3
+ 605	9
+ 892	8
+ 249	6
+ 674	8
+ 319	1
+ 529	7
+ 558	10
+ 478	6
+ 967	6
+ 858	5
+ 820	7
+ 307	0
+ 637	4
+ 852	9
+ 19	9
+ 205	6
+ 869	1
+ 376	1
+ 717	1
+ 917	0
+ 111	4
+ 709	7
+ 420	2
+ 265	4
+ 792	1
+ 838	6
+ 809	1
+ 640	4
+ 506	5
+ 328	5
+ 414	5
+ 148	3
+ 630	5
+ 400	3
+ 576	3
+ 382	7
+ 764	1
+ 356	2
+ 279	6
+ 571	1
+ 743	4
+ 683	6
+ 554	3
+ 998	1
+ 816	3
+ 585	2
+ 858	7
+ 512	5
+ 258	9
+ 835	8
+ 230	2
+ 520	10
+ 308	9
+ 177	6
+ 497	7
+ 659	2
+ 157	3
+ 793	7
+ 665	8
+ 772	5
+ 116	4
+ 711	10
+ 90	2
+ 463	3
+ 136	3
+ 181	4
+ 514	7
+ 359	8
+ 577	5
+ 410	1
+ 285	1
+ 314	4
+ 411	1
+ 153	1
+ 897	9
+ 557	0
+ 281	3
+ 988	4
+ 492	5
+ 719	6
+ 748	9
+ 993	3
+ 601	4
+ 85	2
+ 889	5
+ 251	2
+ 564	6
+ 616	10
+ 672	8
+ 50	6
+ 694	6
+ 582	10
+ 875	6
+ 346	4
+ 21	1
+ 994	8
+ 964	10
+ 31	6
+ 340	1
+ 742	2
+ 610	10
+ 402	2
+ 559	0
+ 148	2
+ 786	2
+ 800	5
+ 805	4
+ 455	7
+ 952	8
+ 48	10
+ 866	0
+ 741	8
+ 29	8
+ 395	4
+ 887	1
+ 597	5
+ 132	10
+ 670	7
+ 17	8
+ 921	8
+ 17	7
+ 283	8
+ 103	7
+ 503	1
+ 541	6
+ 27	4
+ 592	8
+ 238	6
+ 539	6
+ 990	4
+ 771	6
+ 922	9
+ 586	6
+ 593	6
+ 411	5
+ 406	4
+ 235	7
+ 250	3
+ 428	8
+ 393	10
+ 303	4
+ 376	9
+ 188	6
+ 516	7
+ 246	5
+ 153	0
+ 93	1
+ 919	7
+ 667	5
+ 282	1
+ 27	7
+ 506	3
+ 378	8
+ 600	8
+ 509	10
+ 775	8
+ 414	2
+ 707	6
+ 765	2
+ 328	0
+ 729	5
+ 28	8
+ 556	9
+ 501	2
+ 460	8
+ 301	5
+ 471	8
+ 749	8
+ 563	3
+ 655	1
+ 342	4
+ 883	8
+ 582	6
+ 357	3
+ 813	7
+ 357	5
+ 166	4
+ 364	7
+ 333	9
+ 945	8
+ 649	2
+ 280	1
+ 53	0
+ 969	6
+ 377	6
+ 688	7
+ 55	6
+ 476	6
+ 161	8
+ 983	10
+ 519	3
+ 516	7
+ 726	9
+ 407	1
+ 745	4
+ 853	4
+ 598	1
+ 514	7
+ 161	5
+ 268	5
+ 107	10
+ 258	2
+ 527	7
+ 799	7
+ 567	8
+ 663	1
+ 123	2
+ 772	8
+ 59	2
+ 909	8
+ 532	8
+ 197	1
+ 894	7
+ 781	1
+ 193	0
+ 593	3
+ 5	9
+ 463	5
+ 585	3
+ 221	2
+ 45	9
+ 238	2
+ 63	0
+ 18	1
+ 189	9
+ 925	7
+ 688	1
+ 851	6
+ 833	6
+ 636	0
+ 681	2
+ 327	7
+ 80	8
+ 217	7
+ 53	4
+ 817	1
+ 322	1
+ 266	4
+ 66	3
+ 506	3
+ 210	4
+ 976	9
+ 554	8
+ 480	4
+ 458	1
+ 414	1
+ 345	7
+ 824	4
+ 532	0
+ 90	6
+ 480	9
+ 683	8
+ 963	9
+ 187	0
+ 234	7
+ 284	4
+ 124	3
+ 342	7
+ 87	8
+ 66	5
+ 938	5
+ 683	3
+ 221	5
+ 708	8
+ 548	8
+ 338	0
+ 706	0
+ 830	7
+ 971	0
+ 699	2
+ 709	10
+ 649	8
+ 244	10
+ 511	3
+ 813	6
+ 875	8
+ 57	6
+ 34	3
+ 66	7
+ 30	6
+ 541	4
+ 642	2
+ 390	5
+ 917	4
+ 487	6
+ 565	2
+ 600	2
+ 29	8
+ 205	5
+ 174	0
+ 118	0
+ 769	2
+ 608	8
+ 452	7
+ 545	5
+ 288	1
+ 851	9
+ 333	2
+ 401	3
+ 601	9
+ 867	2
+ 84	5
+ 380	1
+ 311	6
+ 654	5
+ 604	8
+ 535	4
+ 947	1
+ 176	4
+ 817	7
+ 881	1
+ 808	7
+ 34	1
+ 972	4
+ 392	6
+ 322	3
+ 740	4
+ 725	1
+ 520	0
+ 706	2
+ 521	3
+ 947	1
+ 683	9
+ 199	9
+ 292	0
+ 581	2
+ 120	4
+ 905	2
+ 529	9
+ 588	9
+ 450	9
+ 179	2
+ 318	9
+ 310	8
+ 941	0
+ 13	5
+ 325	10
+ 518	0
+ 853	7
+ 867	1
+ 732	4
+ 318	9
+ 836	2
+ 6	4
+ 100	6
+ 287	6
+ 505	5
+ 741	8
+ 370	1
+ 661	3
+ 67	7
+ 773	4
+ 633	3
+ 401	5
+ 7	3
+ 631	7
+ 716	9
+ 592	6
+ 173	6
+ 917	3
+ 192	2
+ 824	7
+ 670	6
+ 520	0
+ 616	2
+ 351	7
+ 854	1
+ 75	5
+ 414	5
+ 973	4
+ 744	6
+ 160	5
+ 554	8
+ 11	7
+ 349	9
+ 0	5
+ 132	8
+ 239	8
+ 389	8
+ 842	0
+ 941	2
+ 688	8
+ 317	8
+ 282	7
+ 239	3
+ 151	10
+ 860	3
+ 441	4
+ 62	5
+ 141	4
+ 381	1
+ 952	5
+ 965	2
+ 315	4
+ 950	2
+ 359	9
+ 351	0
+ 686	7
+ 809	10
+ 397	0
+ 224	5
+ 30	1
+ 859	5
+ 497	9
+ 924	6
+ 331	3
+ 779	3
+ 818	7
+ 474	1
+ 99	4
+ 291	5
+ 316	6
+ 504	0
+ 308	3
+ 970	7
+ 361	2
+ 254	4
+ 277	1
+ 863	8
+ 33	8
+ 413	4
+ 93	2
+ 648	9
+ 937	1
+ 44	0
+ 546	3
+ 493	9
+ 976	10
+ 863	3
+ 310	8
+ 991	7
+ 27	2
+ 63	3
+ 358	9
+ 78	4
+ 714	5
+ 755	8
+ 682	4
+ 717	6
+ 525	8
+ 654	1
+ 97	1
+ 933	1
+ 144	8
+ 358	5
+ 629	3
+ 126	7
+ 593	2
+ 959	10
+ 115	0
+ 342	8
+ 527	1
+ 635	2
+ 501	4
+ 828	0
+ 114	5
+ 96	2
+ 630	0
+ 284	8
+ 825	6
+ 228	5
+ 990	4
+ 109	6
+ 542	1
+ 534	7
+ 105	9
+ 485	6
+ 974	1
+ 842	5
+ 473	7
+ 500	6
+ 152	6
+ 798	8
+ 625	1
+ 555	4
+ 723	8
+ 903	7
+ 136	0
+ 296	7
+ 80	8
+ 334	2
+ 706	8
+ 818	7
+ 940	7
+ 154	4
+ 329	7
+ 1000	5
+ 249	8
+ 264	9
+ 879	8
+ 323	6
+ 602	2
+ 314	7
+ 239	6
+ 416	3
+ 439	7
+ 505	1
+ 569	3
+ 825	5
+ 982	10
+ 921	3
+ 633	9
+ 793	9
+ 718	1
+ 756	6
+ 877	1
+ 198	5
+ 306	5
+ 217	5
+ 121	6
+ 864	6
+ 382	4
+ 706	10
+ 691	5
+ 460	7
+ 511	4
+ 985	1
+ 302	8
+ 26	0
+ 835	8
+ 617	7
+ 862	8
+ 191	2
+ 326	4
+ 713	4
+ 41	6
+ 8	4
+ 946	7
+ 375	6
+ 245	8
+ 310	8
+ 216	3
+ 900	5
+ 73	9
+ 538	9
+ 708	2
+ 620	6
+ 970	8
+ 738	3
+ 219	5
+ 743	3
+ 28	8
+ 683	10
+ 465	1
+ 611	7
+ 893	9
+ 466	1
+ 215	4
+ 626	3
+ 291	2
+ 196	10
+ 319	8
+ 569	3
+ 627	3
+ 585	8
+ 758	3
+ 107	8
+ 80	8
+ 759	5
+ 848	4
+ 255	7
+ 291	7
+ 849	5
+ 87	5
+ 794	4
+ 640	10
+ 378	10
+ 807	9
+ 249	4
+ 253	8
+ 281	0
+ 162	4
+ 797	2
+ 177	6
+ 787	0
+ 926	0
+ 766	2
+ 763	6
+ 723	9
+ 92	5
+ 228	7
+ 507	6
+ 692	3
+ 552	9
+ 748	8
+ 775	0
+ 817	9
+ 417	6
+ 179	6
+ 170	10
+ 619	1
+ 7	4
+ 312	8
+ 1	0
+ 621	1
+ 552	8
+ 825	1
+ 455	5
+ 373	0
+ 457	1
+ 813	2
+ 151	6
+ 169	6
+ 243	3
+ 161	4
+ 314	8
+ 508	3
+ 166	8
+ 92	2
+ 856	7
+ 259	4
+ 561	1
+ 467	0
+ 600	8
+ 24	1
+ 961	8
+ 289	1
+ 467	5
+ 679	7
+ 806	8
+ 124	1
+ 621	6
+ 441	8
+ 453	5
+ 954	3
+ 245	2
+ 716	8
+ 297	2
+ 823	9
+ 22	8
+ 955	10
+ 684	2
+ 95	2
+ 702	8
+ 862	5
+ 615	10
+ 628	2
+ 617	1
+ 23	1
+ 602	10
+ 378	8
+ 189	1
+ 654	5
+ 276	5
+ 383	3
+ 323	3
+ 281	0
+ 582	4
+ 159	3
+ 151	0
+ 793	8
+ 6	4
+ 2	6
+ 491	0
+ 693	1
+ 2	1
+ 941	2
+ 164	6
+ 677	4
+ 71	1
+ 737	4
+ 398	0
+ 402	10
+ 395	6
+ 264	5
+ 581	1
+ 312	6
+ 477	3
+ 209	10
+ 340	9
+ 61	3
+ 972	0
+ 532	1
+ 596	2
+ 576	7
+ 269	3
+ 61	7
+ 331	5
+ 647	7
+ 23	9
+ 272	6
+ 967	6
+ 190	4
+ 899	4
+ 413	2
+ 300	5
+ 581	3
+ 475	1
+ 408	1
+ 323	10
+ 738	6
+ 297	8
+ 259	6
+ 262	9
+ 354	3
+ 817	6
+ 890	8
+ 211	1
+ 230	1
+ 479	6
+ 350	8
+ 116	9
+ 52	6
+ 44	5
+ 662	4
+ 443	4
+ 959	7
+ 200	2
+ 368	5
+ 124	7
+ 748	9
+ 349	6
+ 727	6
+ 718	10
+ 671	2
+ 599	0
+ 977	7
+ 952	0
+ 307	10
+ 488	10
+ 363	9
+ 369	3
+ 672	6
+ 540	0
+ 31	7
+ 763	8
+ 606	1
+ 417	3
+ 672	1
+ 289	3
+ 333	9
+ 364	3
+ 604	3
+ 339	9
+ 311	8
+ 879	7
+ 759	2
+ 996	4
+ 817	5
+ 471	8
+ 199	2
+ 627	8
+ 346	0
+ 138	0
+ 180	4
+ 362	5
+ 316	7
+ 823	9
+ 41	2
+ 830	4
+ 989	7
+ 26	7
+ 957	0
+ 179	8
+ 557	7
+ 622	8
+ 885	2
+ 562	2
+ 294	7
+ 250	5
+ 128	6
+ 987	4
+ 337	8
+ 363	4
+ 972	2
+ 730	10
+ 901	8
+ 710	9
+ 777	9
+ 632	3
+ 540	3
+ 91	4
+ 503	7
+ 656	8
+ 353	9
+ 271	5
+ 517	3
+ 924	9
+ 68	3
+ 232	0
+ 480	10
+ 2	4
+ 717	7
+ 240	5
+ 600	9
+ 829	1
+ 126	9
+ 564	6
+ 573	2
+ 426	9
+ 126	7
+ 406	6
+ 955	3
+ 498	0
+ 618	7
+ 62	1
+ 692	1
+ 481	4
+ 775	7
+ 904	4
+ 592	7
+ 515	7
+ 652	1
+ 347	2
+ 300	8
+ 150	4
+ 471	6
+ 69	4
+ 887	6
+ 448	5
+ 297	5
+ 605	10
+ 574	1
+ 398	3
+ 806	3
+ 725	4
+ 34	2
+ 116	7
+ 320	5
+ 911	6
+ 237	1
+ 46	7
+ 619	1
+ 133	5
+ 682	6
+ 12	10
+ 91	6
+ 967	7
+ 702	4
+ 14	5
+ 667	7
+ 905	7
+ 978	0
+ 387	3
+ 484	3
+ 918	7
+ 361	10
+ 429	10
+ 78	6
+ 485	8
+ 143	5
+ 738	2
+ 113	7
+ 899	8
+ 70	9
+ 322	7
+ 651	2
+ 438	6
+ 247	8
+ 927	7
+ 124	8
+ 453	5
+ 808	9
+ 464	9
+ 445	9
+ 646	6
+ 446	4
+ 822	6
+ 89	7
+ 374	2
+ 633	7
+ 897	3
+ 923	3
+ 913	2
+ 160	8
+ 902	3
+ 684	4
+ 768	5
+ 237	2
+ 378	7
+ 181	0
+ 270	6
+ 408	1
+ 187	5
+ 814	6
+ 657	4
+ 257	6
+ 731	2
+ 889	6
+ 350	0
+ 484	3
+ 333	2
+ 607	1
+ 661	8
+ 333	0
+ 527	5
+ 63	8
+ 142	5
+ 890	3
+ 968	7
+ 889	6
+ 151	1
+ 179	9
+ 325	1
+ 526	7
+ 116	0
+ 927	4
+ 178	5
+ 550	8
+ 379	9
+ 877	9
+ 398	9
+ 703	5
+ 410	6
+ 868	4
+ 297	8
+ 3	4
+ 903	2
+ 329	2
+ 250	9
+ 903	4
+ 865	8
+ 815	0
+ 366	4
+ 881	7
+ 248	8
+ 651	6
+ 698	4
+ 185	1
+ 947	1
+ 487	2
+ 810	5
+ 691	7
+ 672	0
+ 940	9
+ 875	8
+ 287	7
diff --git a/contrib/bloom/expected/bloom.out b/contrib/bloom/expected/bloom.out
new file mode 100644
index ...f7334fa
*** a/contrib/bloom/expected/bloom.out
--- b/contrib/bloom/expected/bloom.out
***************
*** 0 ****
--- 1,120 ----
+ CREATE EXTENSION bloom;
+ CREATE TABLE tst (
+ 	i	int4,
+ 	t	text
+ );
+ \copy tst from 'data/data'
+ CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+ SET enable_seqscan=on;
+ SET enable_bitmapscan=off;
+ SET enable_indexscan=off;
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16;
+                 QUERY PLAN                 
+ -------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: (i = 16)
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: (i = 16)
+ (5 rows)
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE t = '5';
+                 QUERY PLAN                 
+ -------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: (t = '5'::text)
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: (t = '5'::text)
+ (5 rows)
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+                         QUERY PLAN                        
+ ----------------------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: ((i = 16) AND (t = '5'::text))
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: ((i = 16) AND (t = '5'::text))
+ (5 rows)
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ VACUUM ANALYZE tst;
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ VACUUM FULL tst;
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ RESET enable_seqscan;
+ RESET enable_bitmapscan;
+ RESET enable_indexscan;
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
new file mode 100644
index ...e5c7a86
*** a/contrib/bloom/sql/bloom.sql
--- b/contrib/bloom/sql/bloom.sql
***************
*** 0 ****
--- 1,46 ----
+ CREATE EXTENSION bloom;
+ 
+ CREATE TABLE tst (
+ 	i	int4,
+ 	t	text
+ );
+ 
+ \copy tst from 'data/data'
+ 
+ CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+ 
+ SET enable_seqscan=on;
+ SET enable_bitmapscan=off;
+ SET enable_indexscan=off;
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16;
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE t = '5';
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ VACUUM ANALYZE tst;
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ VACUUM FULL tst;
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ RESET enable_seqscan;
+ RESET enable_bitmapscan;
+ RESET enable_indexscan;
diff --git a/contrib/bloom/t/001_wal.pl b/contrib/bloom/t/001_wal.pl
new file mode 100644
index ...bbb398b
*** a/contrib/bloom/t/001_wal.pl
--- b/contrib/bloom/t/001_wal.pl
***************
*** 0 ****
--- 1,75 ----
+ # Test generic xlog record work for bloom index replication.
+ use strict;
+ use warnings;
+ use PostgresNode;
+ use TestLib;
+ use Test::More tests => 31;
+ 
+ my $node_master;
+ my $node_standby;
+ 
+ # Run few queries on both master and standby and check their results match.
+ sub test_index_replay
+ {
+ 	my ($test_name) = @_;
+ 
+ 	# Wait for standby to catch up
+ 	my $applname = $node_standby->name;
+ 	my $caughtup_query =
+ 		"SELECT pg_current_xlog_location() <= write_location FROM pg_stat_replication WHERE application_name = '$applname';";
+ 	$node_master->poll_query_until('postgres', $caughtup_query)
+ 	  or die "Timed out while waiting for standby 1 to catch up";
+ 
+ 	my $queries = qq(SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ SELECT * FROM tst WHERE i = 0;
+ SELECT * FROM tst WHERE i = 3;
+ SELECT * FROM tst WHERE t = 'b';
+ SELECT * FROM tst WHERE t = 'f';
+ SELECT * FROM tst WHERE i = 3 AND t = 'c';
+ SELECT * FROM tst WHERE i = 7 AND t = 'e';
+ );
+ 
+ 	# Run test queries and compare their result
+ 	my $master_result = $node_master->psql("postgres", $queries);
+ 	my $standby_result = $node_standby->psql("postgres", $queries);
+ 
+ 	is($master_result, $standby_result, "$test_name: query result matches");
+ }
+ 
+ # Initialize master node
+ $node_master = get_new_node('master');
+ $node_master->init(allows_streaming => 1);
+ $node_master->start;
+ my $backup_name = 'my_backup';
+ 
+ # Take backup
+ $node_master->backup($backup_name);
+ 
+ # Create streaming standby linking to master
+ $node_standby = get_new_node('standby');
+ $node_standby->init_from_backup($node_master, $backup_name,
+ 	has_streaming => 1);
+ $node_standby->start;
+ 
+ # Create some bloom index on master
+ $node_master->psql("postgres", "CREATE EXTENSION bloom;");
+ $node_master->psql("postgres", "CREATE TABLE tst (i int4, t text);");
+ $node_master->psql("postgres", "INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,100000) i;");
+ $node_master->psql("postgres", "CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);");
+ 
+ # Test that queries give same result
+ test_index_replay('initial');
+ 
+ # Run 10 cycles of table modification. Run test queries after each modification.
+ for (my $i = 1; $i <= 10; $i++)
+ {
+ 	$node_master->psql("postgres", "DELETE FROM tst WHERE i = $i;");
+ 	test_index_replay("delete $i");
+ 	$node_master->psql("postgres", "VACUUM tst;");
+ 	test_index_replay("vacuum $i");
+ 	my ($start, $end) = (100001 + ($i - 1) * 10000, 100000 + $i * 10000);
+ 	$node_master->psql("postgres", "INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series($start,$end) i;");
+ 	test_index_replay("insert $i");
+ }
diff --git a/doc/src/sgml/bloom.sgml b/doc/src/sgml/bloom.sgml
new file mode 100644
index ...c207e6d
*** a/doc/src/sgml/bloom.sgml
--- b/doc/src/sgml/bloom.sgml
***************
*** 0 ****
--- 1,218 ----
+ <!-- doc/src/sgml/bloom.sgml -->
+ 
+ <sect1 id="bloom" xreflabel="bloom">
+  <title>bloom</title>
+ 
+  <indexterm zone="bloom">
+   <primary>bloom</primary>
+  </indexterm>
+ 
+  <para>
+   <literal>bloom</> is a contrib which implements index access method.  It comes
+   as example of custom access methods and generic WAL records usage.  But it
+   is also useful itself.
+  </para>
+ 
+  <sect2>
+   <title>Introduction</title>
+ 
+   <para>
+    Implementation of
+    <ulink url="http://en.wikipedia.org/wiki/Bloom_filter">Bloom filter</ulink>
+    allows fast exclusion of non-candidate tuples.
+    Since signature is a lossy representation of all indexed attributes, 
+    search results should be rechecked using heap information. 
+    User can specify signature length (in uint16, default is 5) and the number of 
+    bits, which can be setted, per attribute (1 < colN < 2048).
+   </para>
+ 
+   <para>
+    This index is useful if table has many attributes and queries can include
+    their arbitary combinations.  Traditional <literal>btree</> index is faster
+    than bloom index, but it'd require too many indexes to support all possible 
+    queries, while one need only one bloom index.  Bloom index supports only 
+    equality comparison.  Since it's a signature file, not a tree, it always
+    should be readed fully, but sequentially, so index search performance is 
+    constant and doesn't depend on a query. 
+   </para>
+  </sect2>
+ 
+  <sect2>
+   <title>Parameters</title>
+ 
+   <para>
+    <literal>bloom</> indexes accept following parameters in <literal>WITH</>
+    clause.
+   </para>
+ 
+    <variablelist>
+    <varlistentry>
+     <term><literal>length</></term>
+     <listitem>
+      <para>
+       Length of signature in uint16 type values
+      </para>
+     </listitem>
+    </varlistentry>
+    </variablelist>
+    <variablelist>
+    <varlistentry>
+     <term><literal>col1 &mdash; col16</></term>
+     <listitem>
+      <para>
+       Number of bits for corresponding column
+      </para>
+     </listitem>
+    </varlistentry>
+    </variablelist>
+  </sect2>
+ 
+  <sect2>
+   <title>Examples</title>
+ 
+   <para>
+    Example of index definition is given below.
+   </para>
+ 
+ <programlisting>
+ CREATE INDEX bloomidx ON tbloom(i1,i2,i3) 
+        WITH (length=5, col1=2, col2=2, col3=4);
+ </programlisting>
+ 
+   <para>
+    Here, we create bloom index with signature length 80 bits and attributes
+    i1, i2  mapped to 2 bits, attribute i3 - to 4 bits.
+   </para>
+ 
+   <para>
+    Example of index definition and usage is given below.
+   </para>
+ 
+ <programlisting>
+ CREATE TABLE tbloom AS
+ SELECT
+     random()::int as i1,
+     random()::int as i2,
+     random()::int as i3,
+     random()::int as i4,
+     random()::int as i5,
+     random()::int as i6,
+     random()::int as i7,
+     random()::int as i8,
+     random()::int as i9,
+     random()::int as i10,
+     random()::int as i11,
+     random()::int as i12,
+     random()::int as i13
+ FROM
+     generate_series(1,1000);
+ CREATE INDEX bloomidx ON tbloom USING
+              bloom (i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12);
+ SELECT pg_relation_size('bloomidx');
+ CREATE index btree_idx ON tbloom(i1,i2,i3,i4,i5,i6,i7,i8,i9,i10,i11,i12);
+ SELECT pg_relation_size('btree_idx');
+ </programlisting>
+ 
+ <programlisting>
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                                    QUERY PLAN
+ -----------------------------------------------------------------------------------------------------------------
+  Bitmap Heap Scan on tbloom  (cost=1.50..5.52 rows=1 width=52) (actual time=0.057..0.057 rows=0 loops=1)
+    Recheck Cond: ((i2 = 20) AND (i10 = 15))
+    ->  Bitmap Index Scan on bloomidx  (cost=0.00..1.50 rows=1 width=0) (actual time=0.041..0.041 rows=9 loops=1)
+          Index Cond: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.081 ms
+ (5 rows)
+ </programlisting>
+ 
+   <para>
+    Seqscan is slow.
+   </para>
+ 
+ <programlisting>
+ =# SET enable_bitmapscan = off;
+ =# SET enable_indexscan = off;
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                             QUERY PLAN
+ --------------------------------------------------------------------------------------------------
+  Seq Scan on tbloom  (cost=0.00..25.00 rows=1 width=52) (actual time=0.162..0.162 rows=0 loops=1)
+    Filter: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.181 ms
+ (3 rows)
+ </programlisting>
+ 
+  <para>
+   Btree index will be not used for this query.
+  </para>
+ 
+ <programlisting>
+ =# DROP INDEX bloomidx;
+ =# CREATE INDEX btree_idx ON tbloom(i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12);
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                             QUERY PLAN
+ --------------------------------------------------------------------------------------------------
+  Seq Scan on tbloom (cost=0.00..25.00 rows=1 width=52) (actual time=0.210..0.210 rows=0 loops=1)
+    Filter: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.250 ms
+ (3 rows)
+ </programlisting>
+  </sect2>
+ 
+  <sect2>
+   <title>Opclass interface</title>
+ 
+   <para>
+    Bloom opclass interface is simple.  It requires 1 supporting function:
+    hash function for indexing datatype.  And it provides 1 search operator:
+    equality operator.  The example below shows <literal>opclass</> definition
+    for <literal>text</> datatype.
+   </para>
+ 
+ <programlisting>
+ CREATE OPERATOR CLASS text_ops
+ DEFAULT FOR TYPE text USING bloom AS
+     OPERATOR    1   =(text, text),
+     FUNCTION    1   hashtext(text);
+ </programlisting>
+  </sect2>
+ 
+  <sect2>
+   <title>Limitation</title>
+   <para>
+ 
+    <itemizedlist>
+     <listitem>
+      <para>
+       For now, only opclasses for <literal>int4</>, <literal>text</> comes
+       with contrib.  However, users may define more of them.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       Only <literal>=</literal> operator is supported for search now.  But it's
+       possible to add support of arrays with contains and intersection
+       operations in future.
+      </para>
+     </listitem>
+    </itemizedlist>
+   </para>
+  </sect2>
+ 
+  <sect2>
+   <title>Authors</title>
+ 
+   <para>
+    Teodor Sigaev <email>teodor@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+ 
+   <para>
+    Alexander Korotkov <email>a.korotkov@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+ 
+   <para>
+    Oleg Bartunov <email>obartunov@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+  </sect2>
+ 
+ </sect1>
diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
new file mode 100644
index 4e3f337..c8708ec
*** a/doc/src/sgml/contrib.sgml
--- b/doc/src/sgml/contrib.sgml
*************** CREATE EXTENSION <replaceable>module_nam
*** 105,110 ****
--- 105,111 ----
   &adminpack;
   &auth-delay;
   &auto-explain;
+  &bloom;
   &btree-gin;
   &btree-gist;
   &chkpass;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
new file mode 100644
index 30adece..7c73206
*** a/doc/src/sgml/filelist.sgml
--- b/doc/src/sgml/filelist.sgml
***************
*** 106,111 ****
--- 106,112 ----
  <!ENTITY adminpack       SYSTEM "adminpack.sgml">
  <!ENTITY auth-delay      SYSTEM "auth-delay.sgml">
  <!ENTITY auto-explain    SYSTEM "auto-explain.sgml">
+ <!ENTITY bloom           SYSTEM "bloom.sgml">
  <!ENTITY btree-gin       SYSTEM "btree-gin.sgml">
  <!ENTITY btree-gist      SYSTEM "btree-gist.sgml">
  <!ENTITY chkpass         SYSTEM "chkpass.sgml">
#54Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Alexander Korotkov (#53)
Re: WIP: Access method extendability

Hi. As I just said to Tomas Vondra: since your patch creates a new
object type, please make sure to add a case to cover it in the
object_address.sql test. That verifies some things such as
pg_identify_object and related.

Thanks,

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#55Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Alvaro Herrera (#54)
3 attachment(s)
Re: WIP: Access method extendability

Hi!

On Wed, Mar 9, 2016 at 3:27 PM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:

Hi. As I just said to Tomas Vondra: since your patch creates a new
object type, please make sure to add a case to cover it in the
object_address.sql test. That verifies some things such as
pg_identify_object and related.

Good catch, thanks! Tests were added.
I also introduced numbering into patch names to make evident the order to
their application.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

Attachments:

0001-create-am.11.patchapplication/octet-stream; name=0001-create-am.11.patchDownload
diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml
new file mode 100644
index 5f7befb..e5f8f1c
*** a/doc/src/sgml/indexam.sgml
--- b/doc/src/sgml/indexam.sgml
***************
*** 58,63 ****
--- 58,69 ----
    </para>
  
    <para>
+    Index access access methods can be defined and dropped using
+    <xref linkend="sql-createaccessmethod"> and
+     <xref linkend="sql-dropaccessmethod"> SQL commands respectively.
+   </para>
+ 
+   <para>
     An index access method handler function must be declared to accept a
     single argument of type <type>internal</> and to return the
     pseudo-type <type>index_am_handler</>.  The argument is a dummy value that
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
new file mode 100644
index bf95453..77667bd
*** a/doc/src/sgml/ref/allfiles.sgml
--- b/doc/src/sgml/ref/allfiles.sgml
*************** Complete list of usable sgml source file
*** 52,57 ****
--- 52,58 ----
  <!ENTITY commit             SYSTEM "commit.sgml">
  <!ENTITY commitPrepared     SYSTEM "commit_prepared.sgml">
  <!ENTITY copyTable          SYSTEM "copy.sgml">
+ <!ENTITY createAccessMethod SYSTEM "create_access_method.sgml">
  <!ENTITY createAggregate    SYSTEM "create_aggregate.sgml">
  <!ENTITY createCast         SYSTEM "create_cast.sgml">
  <!ENTITY createCollation    SYSTEM "create_collation.sgml">
*************** Complete list of usable sgml source file
*** 94,99 ****
--- 95,101 ----
  <!ENTITY delete             SYSTEM "delete.sgml">
  <!ENTITY discard            SYSTEM "discard.sgml">
  <!ENTITY do                 SYSTEM "do.sgml">
+ <!ENTITY dropAccessMethod   SYSTEM "drop_access_method.sgml">
  <!ENTITY dropAggregate      SYSTEM "drop_aggregate.sgml">
  <!ENTITY dropCast           SYSTEM "drop_cast.sgml">
  <!ENTITY dropCollation      SYSTEM "drop_collation.sgml">
diff --git a/doc/src/sgml/ref/create_access_method.sgml b/doc/src/sgml/ref/create_access_method.sgml
new file mode 100644
index ...f255ecc
*** a/doc/src/sgml/ref/create_access_method.sgml
--- b/doc/src/sgml/ref/create_access_method.sgml
***************
*** 0 ****
--- 1,120 ----
+ <!--
+ doc/src/sgml/ref/create_access_method.sgml
+ PostgreSQL documentation
+ -->
+ 
+ <refentry id="SQL-CREATEACCESSMETHOD">
+  <indexterm zone="sql-createaccessmethod">
+   <primary>CREATE ACCESS METHOD</primary>
+  </indexterm>
+ 
+  <refmeta>
+   <refentrytitle>CREATE ACCESS METHOD</refentrytitle>
+   <manvolnum>7</manvolnum>
+   <refmiscinfo>SQL - Language Statements</refmiscinfo>
+  </refmeta>
+ 
+  <refnamediv>
+   <refname>CREATE ACCESS METHOD</refname>
+   <refpurpose>define a new access method</refpurpose>
+  </refnamediv>
+ 
+  <refsynopsisdiv>
+ <synopsis>
+ CREATE ACCESS METHOD <replaceable class="parameter">name</replaceable>
+     TYPE INDEX
+     HANDLER <replaceable class="parameter">handler_function</replaceable>
+ </synopsis>
+  </refsynopsisdiv>
+ 
+  <refsect1>
+   <title>Description</title>
+ 
+   <para>
+    <command>CREATE ACCESS METHOD</command> creates a new access method.
+   </para>
+ 
+   <para>
+    The access method name must be unique within the database.
+   </para>
+ 
+   <para>
+    Only superusers can define new access methods.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Parameters</title>
+ 
+   <variablelist>
+    <varlistentry>
+     <term><replaceable class="parameter">name</replaceable></term>
+     <listitem>
+      <para>
+       The name of the access method to be created.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>TYPE INDEX</literal></term>
+     <listitem>
+      <para>
+       This clause specifies type of access method to define.
+       For now, there are only index access methods.  But intentionally
+       there would be other types of access methods.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>HANDLER <replaceable class="parameter">handler_function</replaceable></literal></term>
+     <listitem>
+      <para><replaceable class="parameter">handler_function</replaceable> is the
+       name of a previously registered function that will be called to
+       retrieve the struct which contains required parameters and functions
+       of access method to the core.  The handler function must take single
+       argument of type <type>internal</>, and its return type must be
+       <type>index_am_handler</type>.
+      </para>
+ 
+      <para>
+       See <xref linkend="index-api"> for index access methods API.
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Examples</title>
+ 
+   <para>
+    Create an access method <literal>bloom</> with
+    handler function <literal>blhandler</>:
+ <programlisting>
+ CREATE ACCESS METHOD bloom TYPE INDEX HANDLER blhandler;
+ </programlisting>
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Compatibility</title>
+ 
+   <para>
+    <command>CREATE ACCESS METHOD</command> is a
+    <productname>PostgreSQL</> extension.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>See Also</title>
+ 
+   <simplelist type="inline">
+    <member><xref linkend="sql-dropaccessmethod"></member>
+    <member><xref linkend="sql-createopclass"></member>
+    <member><xref linkend="sql-createopfamily"></member>
+   </simplelist>
+  </refsect1>
+ 
+ </refentry>
diff --git a/doc/src/sgml/ref/drop_access_method.sgml b/doc/src/sgml/ref/drop_access_method.sgml
new file mode 100644
index ...354b923
*** a/doc/src/sgml/ref/drop_access_method.sgml
--- b/doc/src/sgml/ref/drop_access_method.sgml
***************
*** 0 ****
--- 1,112 ----
+ <!--
+ doc/src/sgml/ref/drop_access_method.sgml
+ PostgreSQL documentation
+ -->
+ 
+ <refentry id="SQL-DROPACCESSMETHOD">
+  <indexterm zone="sql-dropaccessmethod">
+   <primary>DROP ACCESS METHOD</primary>
+  </indexterm>
+ 
+  <refmeta>
+   <refentrytitle>DROP ACCESS METHOD</refentrytitle>
+   <manvolnum>7</manvolnum>
+   <refmiscinfo>SQL - Language Statements</refmiscinfo>
+  </refmeta>
+ 
+  <refnamediv>
+   <refname>DROP ACCESS METHOD</refname>
+   <refpurpose>remove an access method</refpurpose>
+  </refnamediv>
+ 
+  <refsynopsisdiv>
+ <synopsis>
+ DROP ACCESS METHOD [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ CASCADE | RESTRICT ]
+ </synopsis>
+  </refsynopsisdiv>
+ 
+  <refsect1>
+   <title>Description</title>
+ 
+   <para>
+    <command>DROP ACCESS METHOD</command> removes an existing access method.
+    Only superusers can drop access methods.
+   </para>
+ 
+   <para>
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Parameters</title>
+ 
+   <variablelist>
+    <varlistentry>
+     <term><literal>IF EXISTS</literal></term>
+     <listitem>
+      <para>
+       Do not throw an error if the access method does not exist.
+       A notice is issued in this case.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><replaceable class="parameter">name</replaceable></term>
+     <listitem>
+      <para>
+       The name of an existing access method.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>CASCADE</literal></term>
+     <listitem>
+      <para>
+       Automatically drop objects that depend on the access method
+       (such as operator classes, operator families, indexes).
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>RESTRICT</literal></term>
+     <listitem>
+      <para>
+       Refuse to drop the access method if any objects depend on it.
+       This is the default.
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Examples</title>
+ 
+   <para>
+    Drop the access method <literal>bloom</>:
+ <programlisting>
+ DROP ACCESS METHOD bloom;
+ </programlisting></para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Compatibility</title>
+ 
+   <para>
+    <command>DROP ACCESS METHOD</command> is a
+    <productname>PostgreSQL</> extension.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>See Also</title>
+ 
+   <simplelist type="inline">
+    <member><xref linkend="sql-createaccessmethod"></member>
+   </simplelist>
+  </refsect1>
+ 
+ </refentry>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
new file mode 100644
index 03020df..8acdff1
*** a/doc/src/sgml/reference.sgml
--- b/doc/src/sgml/reference.sgml
***************
*** 80,85 ****
--- 80,86 ----
     &commit;
     &commitPrepared;
     &copyTable;
+    &createAccessMethod;
     &createAggregate;
     &createCast;
     &createCollation;
***************
*** 122,127 ****
--- 123,129 ----
     &delete;
     &discard;
     &do;
+    &dropAccessMethod;
     &dropAggregate;
     &dropCast;
     &dropCollation;
diff --git a/src/backend/access/index/amapi.c b/src/backend/access/index/amapi.c
new file mode 100644
index bda166a..e9b9b3f
*** a/src/backend/access/index/amapi.c
--- b/src/backend/access/index/amapi.c
*************** GetIndexAmRoutineByAmId(Oid amoid)
*** 62,67 ****
--- 62,74 ----
  			 amoid);
  	amform = (Form_pg_am) GETSTRUCT(tuple);
  
+ 	/* Check if it's index access method */
+ 	if (amform->amtype != AMTYPE_INDEX)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("access method \"%s\" type is not index",
+ 						NameStr(amform->amname))));
+ 
  	amhandler = amform->amhandler;
  
  	/* Complain if handler OID is invalid */
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
new file mode 100644
index c48e37b..a86a488
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 20,25 ****
--- 20,26 ----
  #include "catalog/heap.h"
  #include "catalog/index.h"
  #include "catalog/objectaccess.h"
+ #include "catalog/pg_am.h"
  #include "catalog/pg_amop.h"
  #include "catalog/pg_amproc.h"
  #include "catalog/pg_attrdef.h"
*************** static const Oid object_classes[] = {
*** 160,166 ****
  	ExtensionRelationId,		/* OCLASS_EXTENSION */
  	EventTriggerRelationId,		/* OCLASS_EVENT_TRIGGER */
  	PolicyRelationId,			/* OCLASS_POLICY */
! 	TransformRelationId			/* OCLASS_TRANSFORM */
  };
  
  
--- 161,168 ----
  	ExtensionRelationId,		/* OCLASS_EXTENSION */
  	EventTriggerRelationId,		/* OCLASS_EVENT_TRIGGER */
  	PolicyRelationId,			/* OCLASS_POLICY */
! 	TransformRelationId,		/* OCLASS_TRANSFORM */
! 	AccessMethodRelationId		/* OCLASS_AM */
  };
  
  
*************** doDeletion(const ObjectAddress *object, 
*** 1270,1275 ****
--- 1272,1280 ----
  
  		case OCLASS_TRANSFORM:
  			DropTransformById(object->objectId);
+ 
+ 		case OCLASS_AM:
+ 			RemoveAccessMethodById(object->objectId);
  			break;
  
  		default:
*************** getObjectClass(const ObjectAddress *obje
*** 2415,2420 ****
--- 2420,2428 ----
  
  		case TransformRelationId:
  			return OCLASS_TRANSFORM;
+ 
+ 		case AccessMethodRelationId:
+ 			return OCLASS_AM;
  	}
  
  	/* shouldn't get here */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
new file mode 100644
index d2aaa6d..78eff1e
*** a/src/backend/catalog/objectaddress.c
--- b/src/backend/catalog/objectaddress.c
*************** static const ObjectPropertyType ObjectPr
*** 438,443 ****
--- 438,455 ----
  		Anum_pg_type_typacl,
  		ACL_KIND_TYPE,
  		true
+ 	},
+ 	{
+ 		AccessMethodRelationId,
+ 		AmOidIndexId,
+ 		AMOID,
+ 		AMNAME,
+ 		Anum_pg_am_amname,
+ 		InvalidAttrNumber,
+ 		InvalidAttrNumber,
+ 		InvalidAttrNumber,
+ 		-1,
+ 		true
  	}
  };
  
*************** static const struct object_type_map
*** 640,645 ****
--- 652,661 ----
  	/* OCLASS_TRANSFORM */
  	{
  		"transform", OBJECT_TRANSFORM
+ 	},
+ 	/* OCLASS_AM */
+ 	{
+ 		"access method", OBJECT_ACCESS_METHOD
  	}
  };
  
*************** static ObjectAddress get_object_address_
*** 674,679 ****
--- 690,697 ----
  							   List *objargs, bool missing_ok);
  static ObjectAddress get_object_address_defacl(List *objname, List *objargs,
  						  bool missing_ok);
+ static ObjectAddress get_object_address_am(ObjectType objtype, List *objname,
+ 						bool missing_ok);
  static const ObjectPropertyType *get_object_property_data(Oid class_id);
  
  static void getRelationDescription(StringInfo buffer, Oid relid);
*************** get_object_address(ObjectType objtype, L
*** 913,918 ****
--- 931,939 ----
  				address = get_object_address_defacl(objname, objargs,
  													missing_ok);
  				break;
+ 			case OBJECT_ACCESS_METHOD:
+ 				address = get_object_address_am(objtype, objname, missing_ok);
+ 				break;
  			default:
  				elog(ERROR, "unrecognized objtype: %d", (int) objtype);
  				/* placate compiler, in case it thinks elog might return */
*************** get_object_address_opcf(ObjectType objty
*** 1489,1495 ****
  	ObjectAddress address;
  
  	/* XXX no missing_ok support here */
! 	amoid = get_am_oid(strVal(linitial(objname)), false);
  	objname = list_copy_tail(objname, 1);
  
  	switch (objtype)
--- 1510,1516 ----
  	ObjectAddress address;
  
  	/* XXX no missing_ok support here */
! 	amoid = get_am_oid(strVal(linitial(objname)), AMTYPE_INDEX, false);
  	objname = list_copy_tail(objname, 1);
  
  	switch (objtype)
*************** get_object_address_opcf(ObjectType objty
*** 1516,1521 ****
--- 1537,1601 ----
  }
  
  /*
+  * Find the ObjectAddress for an access method.
+  */
+ static ObjectAddress
+ get_object_address_am(ObjectType objtype, List *objname, bool missing_ok)
+ {
+ 	ObjectAddress	address;
+ 	char		   *amname, *catalogname;
+ 	Type		tup;
+ 
+ 	switch (list_length(objname))
+ 	{
+ 		case 1:
+ 			amname = strVal(linitial(objname));
+ 			break;
+ 		case 2:
+ 			catalogname = strVal(linitial(objname));
+ 			amname = strVal(lsecond(objname));
+ 
+ 			/*
+ 			 * We check the catalog name and then ignore it.
+ 			 */
+ 			if (strcmp(catalogname, get_database_name(MyDatabaseId)) != 0)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				  errmsg("cross-database references are not implemented: %s",
+ 						 NameListToString(objname))));
+ 			break;
+ 		default:
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_SYNTAX_ERROR),
+ 				errmsg("improper access method name (too many dotted names): %s",
+ 					   NameListToString(objname))));
+ 			break;
+ 	}
+ 
+ 	address.classId = AccessMethodRelationId;
+ 	address.objectId = InvalidOid;
+ 	address.objectSubId = 0;
+ 
+ 	tup = SearchSysCache1(AMNAME, PointerGetDatum(amname));
+ 	if (!HeapTupleIsValid(tup))
+ 	{
+ 		/* Access method is missing, report error if needed */
+ 		if (!missing_ok)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 					 errmsg("access method \"%s\" does not exist",
+ 							amname)));
+ 		return address;
+ 	}
+ 
+ 	/* Access method is found, return its oid */
+ 	address.objectId = HeapTupleGetOid(tup);
+ 	ReleaseSysCache(tup);
+ 
+ 	return address;
+ }
+ 
+ /*
   * Find the ObjectAddress for an opclass/opfamily member.
   *
   * (The returned address corresponds to a pg_amop/pg_amproc object).
*************** check_object_ownership(Oid roleid, Objec
*** 2179,2184 ****
--- 2259,2265 ----
  			break;
  		case OBJECT_TSPARSER:
  		case OBJECT_TSTEMPLATE:
+ 		case OBJECT_ACCESS_METHOD:
  			/* We treat these object types as being owned by superusers */
  			if (!superuser_arg(roleid))
  				ereport(ERROR,
*************** getObjectDescription(const ObjectAddress
*** 3129,3134 ****
--- 3210,3230 ----
  				break;
  			}
  
+ 		case OCLASS_AM:
+ 			{
+ 				HeapTuple	tup;
+ 
+ 				tup = SearchSysCache1(AMOID,
+ 									  ObjectIdGetDatum(object->objectId));
+ 				if (!HeapTupleIsValid(tup))
+ 					elog(ERROR, "cache lookup failed for access method %u",
+ 						 object->objectId);
+ 				appendStringInfo(&buffer, _("access method %s"),
+ 							NameStr(((Form_pg_am) GETSTRUCT(tup))->amname));
+ 				ReleaseSysCache(tup);
+ 				break;
+ 			}
+ 
  		default:
  			appendStringInfo(&buffer, "unrecognized object %u %u %d",
  							 object->classId,
*************** getObjectTypeDescription(const ObjectAdd
*** 3610,3615 ****
--- 3706,3715 ----
  			appendStringInfoString(&buffer, "transform");
  			break;
  
+ 		case OCLASS_AM:
+ 			appendStringInfoString(&buffer, "access method");
+ 			break;
+ 
  		default:
  			appendStringInfo(&buffer, "unrecognized %u", object->classId);
  			break;
*************** getObjectIdentityParts(const ObjectAddre
*** 4566,4571 ****
--- 4666,4685 ----
  			}
  			break;
  
+ 		case OCLASS_AM:
+ 			{
+ 				char	   *amname;
+ 
+ 				amname = get_am_name(object->objectId);
+ 				if (!amname)
+ 					elog(ERROR, "cache lookup failed for access method %u",
+ 						 object->objectId);
+ 				appendStringInfoString(&buffer, quote_identifier(amname));
+ 				if (objname)
+ 					*objname = list_make1(amname);
+ 			}
+ 			break;
+ 
  		default:
  			appendStringInfo(&buffer, "unrecognized object %u %u %d",
  							 object->classId,
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
new file mode 100644
index b1ac704..6b3742c
*** a/src/backend/commands/Makefile
--- b/src/backend/commands/Makefile
*************** subdir = src/backend/commands
*** 12,18 ****
  top_builddir = ../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o  \
  	collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
  	dbcommands.o define.o discard.o dropcmds.o \
  	event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
--- 12,18 ----
  top_builddir = ../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
  	collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
  	dbcommands.o define.o discard.o dropcmds.o \
  	event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
new file mode 100644
index ...3d7f5ec
*** a/src/backend/commands/amcmds.c
--- b/src/backend/commands/amcmds.c
***************
*** 0 ****
--- 1,277 ----
+ /*-------------------------------------------------------------------------
+  *
+  * amcmds.c
+  *	  Routines for SQL commands that manipulate access methods.
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *
+  * IDENTIFICATION
+  *	  src/backend/commands/amcmds.c
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "access/heapam.h"
+ #include "access/htup_details.h"
+ #include "access/xact.h"
+ #include "catalog/binary_upgrade.h"
+ #include "catalog/catalog.h"
+ #include "catalog/dependency.h"
+ #include "catalog/heap.h"
+ #include "catalog/indexing.h"
+ #include "catalog/objectaccess.h"
+ #include "catalog/pg_authid.h"
+ #include "catalog/pg_am.h"
+ #include "catalog/pg_collation.h"
+ #include "catalog/pg_constraint.h"
+ #include "catalog/pg_depend.h"
+ #include "catalog/pg_enum.h"
+ #include "catalog/pg_language.h"
+ #include "catalog/pg_namespace.h"
+ #include "catalog/pg_proc.h"
+ #include "catalog/pg_proc_fn.h"
+ #include "catalog/pg_range.h"
+ #include "catalog/pg_type.h"
+ #include "catalog/pg_type_fn.h"
+ #include "commands/dbcommands.h"
+ #include "commands/defrem.h"
+ #include "commands/tablecmds.h"
+ #include "commands/typecmds.h"
+ #include "executor/executor.h"
+ #include "miscadmin.h"
+ #include "nodes/makefuncs.h"
+ #include "optimizer/planner.h"
+ #include "optimizer/var.h"
+ #include "parser/parse_coerce.h"
+ #include "parser/parse_collate.h"
+ #include "parser/parse_expr.h"
+ #include "parser/parse_func.h"
+ #include "parser/parse_type.h"
+ #include "utils/acl.h"
+ #include "utils/builtins.h"
+ #include "utils/fmgroids.h"
+ #include "utils/lsyscache.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ #include "utils/snapmgr.h"
+ #include "utils/syscache.h"
+ #include "utils/tqual.h"
+ 
+ /*
+  * Convert a handler function name passed from the parser to an Oid. This
+  * function either return valid function Oid or throw an error.
+  */
+ static Oid
+ lookup_index_am_handler_func(List *handler_name)
+ {
+ 	Oid			handlerOid;
+ 	Oid			funcargtypes[1] = {INTERNALOID};
+ 
+ 	if (handler_name == NIL)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
+ 				 errmsg("handler function is not specified")));
+ 
+ 	/* handlers have no arguments */
+ 	handlerOid = LookupFuncName(handler_name, 1, funcargtypes, false);
+ 
+ 	/* check that handler has correct return type */
+ 	if (get_func_rettype(handlerOid) != INDEX_AM_HANDLEROID)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 				 errmsg("function %s must return type \"index_am_handler\"",
+ 						NameListToString(handler_name))));
+ 
+ 	return handlerOid;
+ }
+ 
+ 
+ /*
+  * CreateAcessMethod
+  *		Registers a new access method.
+  */
+ ObjectAddress
+ CreateAccessMethod(CreateAmStmt *stmt)
+ {
+ 	Relation		rel;
+ 	ObjectAddress	myself;
+ 	ObjectAddress	referenced;
+ 	Oid				amoid;
+ 	Oid				amhandler;
+ 	bool			nulls[Natts_pg_am];
+ 	Datum			values[Natts_pg_am];
+ 	HeapTuple		tup;
+ 
+ 	rel = heap_open(AccessMethodRelationId, RowExclusiveLock);
+ 
+ 	/* Must be super user */
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 			errmsg("permission denied to create access method \"%s\"",
+ 				   stmt->amname),
+ 			errhint("Must be superuser to create access method.")));
+ 
+ 	/* Check if name is busy */
+ 	amoid = GetSysCacheOid1(AMNAME, CStringGetDatum(stmt->amname));
+ 	if (OidIsValid(amoid))
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_DUPLICATE_OBJECT),
+ 				 errmsg("access method \"%s\" already exists", stmt->amname)));
+ 	}
+ 
+ 	/*
+ 	 * Get handler function oid. Handler signature depends on access method
+ 	 * type.
+ 	 */
+ 	switch(stmt->amtype)
+ 	{
+ 		case AMTYPE_INDEX:
+ 			amhandler = lookup_index_am_handler_func(stmt->handler_name);
+ 			break;
+ 		default:
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 					 errmsg("wrong access method type \"%c\"", stmt->amtype)));
+ 			break;
+ 	}
+ 
+ 	/*
+ 	 * Insert tuple into pg_am.
+ 	 */
+ 	memset(values, 0, sizeof(values));
+ 	memset(nulls, false, sizeof(nulls));
+ 
+ 	values[Anum_pg_am_amname - 1] =
+ 		DirectFunctionCall1(namein, CStringGetDatum(stmt->amname));
+ 	values[Anum_pg_am_amhandler - 1] = ObjectIdGetDatum(amhandler);
+ 	values[Anum_pg_am_amtype - 1] = CharGetDatum(stmt->amtype);
+ 
+ 	tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+ 
+ 	amoid = simple_heap_insert(rel, tup);
+ 	CatalogUpdateIndexes(rel, tup);
+ 	heap_freetuple(tup);
+ 
+ 	myself.classId = AccessMethodRelationId;
+ 	myself.objectId = amoid;
+ 	myself.objectSubId = 0;
+ 
+ 	/* Record dependecy on handler function */
+ 	referenced.classId = ProcedureRelationId;
+ 	referenced.objectId = amhandler;
+ 	referenced.objectSubId = 0;
+ 
+ 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ 
+ 	recordDependencyOnCurrentExtension(&myself, false);
+ 
+ 	heap_close(rel, RowExclusiveLock);
+ 
+ 	return myself;
+ }
+ 
+ /*
+  * Guts of access method deletion.
+  */
+ void
+ RemoveAccessMethodById(Oid amOid)
+ {
+ 	Relation	relation;
+ 	HeapTuple	tup;
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 errmsg("must be superuser to drop access methods")));
+ 
+ 	relation = heap_open(AccessMethodRelationId, RowExclusiveLock);
+ 
+ 	tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
+ 	if (!HeapTupleIsValid(tup))
+ 		elog(ERROR, "cache lookup failed for access method %u", amOid);
+ 
+ 	simple_heap_delete(relation, &tup->t_self);
+ 
+ 	ReleaseSysCache(tup);
+ 
+ 	heap_close(relation, RowExclusiveLock);
+ }
+ 
+ /*
+  * Convert single charater access method type into string for error reporting.
+  */
+ static char *
+ get_am_type_string(char amtype)
+ {
+ 	switch (amtype)
+ 	{
+ 		case AMTYPE_INDEX:
+ 			return "index";
+ 		default:
+ 			elog(ERROR, "invalid access method type '%c'", amtype);
+ 	}
+ }
+ 
+ /*
+  * get_am_oid - given an access method name and type, look up the OID
+  *
+  * If missing_ok is false, throw an error if access method not found.  If
+  * true, just return InvalidOid.
+  */
+ Oid
+ get_am_oid(const char *amname, char amtype, bool missing_ok)
+ {
+ 	HeapTuple	tup;
+ 	Oid			oid = InvalidOid;
+ 
+ 	tup = SearchSysCache1(AMNAME, CStringGetDatum(amname));
+ 	if (HeapTupleIsValid(tup))
+ 	{
+ 		Form_pg_am amform = (Form_pg_am) GETSTRUCT(tup);
+ 
+ 		if (amform->amtype != amtype)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 					 errmsg("access method \"%s\" type is not %s",
+ 							NameStr(amform->amname),
+ 							get_am_type_string(amtype))));
+ 
+ 		oid = HeapTupleGetOid(tup);
+ 		ReleaseSysCache(tup);
+ 	}
+ 
+ 	if (!OidIsValid(oid) && !missing_ok)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 				 errmsg("access method \"%s\" does not exist", amname)));
+ 	return oid;
+ }
+ 
+ /*
+  * get_am_name - given an access method OID name and type, look up the name
+  *
+  * Access method type is not required for lookup.  However it's useful to check
+  * the type to ensure it is what we're looking for.
+  */
+ char *
+ get_am_name(Oid amOid)
+ {
+ 	HeapTuple	tup;
+ 	char	   *result = NULL;
+ 
+ 	tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
+ 	if (HeapTupleIsValid(tup))
+ 	{
+ 		Form_pg_am amform = (Form_pg_am) GETSTRUCT(tup);
+ 
+ 		result = pstrdup(NameStr(amform->amname));
+ 		ReleaseSysCache(tup);
+ 	}
+ 	return result;
+ }
+ 
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
new file mode 100644
index 9e32f8d..3f52ad8
*** a/src/backend/commands/event_trigger.c
--- b/src/backend/commands/event_trigger.c
*************** typedef enum
*** 86,91 ****
--- 86,92 ----
  
  /* XXX merge this with ObjectTypeMap? */
  static event_trigger_support_data event_trigger_support[] = {
+ 	{"ACCESS METHOD", true},
  	{"AGGREGATE", true},
  	{"CAST", true},
  	{"CONSTRAINT", true},
*************** EventTriggerSupportsObjectType(ObjectTyp
*** 1078,1083 ****
--- 1079,1085 ----
  		case OBJECT_EVENT_TRIGGER:
  			/* no support for event triggers on event triggers */
  			return false;
+ 		case OBJECT_ACCESS_METHOD:
  		case OBJECT_AGGREGATE:
  		case OBJECT_AMOP:
  		case OBJECT_AMPROC:
*************** EventTriggerSupportsObjectClass(ObjectCl
*** 1167,1172 ****
--- 1169,1175 ----
  		case OCLASS_DEFACL:
  		case OCLASS_EXTENSION:
  		case OCLASS_POLICY:
+ 		case OCLASS_AM:
  			return true;
  	}
  
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
new file mode 100644
index 8a66196..19dac98
*** a/src/backend/commands/opclasscmds.c
--- b/src/backend/commands/opclasscmds.c
*************** DefineOpClass(CreateOpClassStmt *stmt)
*** 678,683 ****
--- 678,689 ----
  	myself.objectId = opclassoid;
  	myself.objectSubId = 0;
  
+ 	/* dependency on access method */
+ 	referenced.classId = AccessMethodRelationId;
+ 	referenced.objectId = amoid;
+ 	referenced.objectSubId = 0;
+ 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ 
  	/* dependency on namespace */
  	referenced.classId = NamespaceRelationId;
  	referenced.objectId = namespaceoid;
*************** DefineOpFamily(CreateOpFamilyStmt *stmt)
*** 743,749 ****
  					   get_namespace_name(namespaceoid));
  
  	/* Get access method OID, throwing an error if it doesn't exist. */
! 	amoid = get_am_oid(stmt->amname, false);
  
  	/* XXX Should we make any privilege check against the AM? */
  
--- 749,755 ----
  					   get_namespace_name(namespaceoid));
  
  	/* Get access method OID, throwing an error if it doesn't exist. */
! 	amoid = get_am_oid(stmt->amname, AMTYPE_INDEX, false);
  
  	/* XXX Should we make any privilege check against the AM? */
  
*************** RemoveAmProcEntryById(Oid entryOid)
*** 1663,1683 ****
  	heap_close(rel, RowExclusiveLock);
  }
  
- char *
- get_am_name(Oid amOid)
- {
- 	HeapTuple	tup;
- 	char	   *result = NULL;
- 
- 	tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
- 	if (HeapTupleIsValid(tup))
- 	{
- 		result = pstrdup(NameStr(((Form_pg_am) GETSTRUCT(tup))->amname));
- 		ReleaseSysCache(tup);
- 	}
- 	return result;
- }
- 
  /*
   * Subroutine for ALTER OPERATOR CLASS SET SCHEMA/RENAME
   *
--- 1669,1674 ----
*************** IsThereOpFamilyInNamespace(const char *o
*** 1723,1744 ****
  						get_am_name(opfmethod),
  						get_namespace_name(opfnamespace))));
  }
- 
- /*
-  * get_am_oid - given an access method name, look up the OID
-  *
-  * If missing_ok is false, throw an error if access method not found.  If
-  * true, just return InvalidOid.
-  */
- Oid
- get_am_oid(const char *amname, bool missing_ok)
- {
- 	Oid			oid;
- 
- 	oid = GetSysCacheOid1(AMNAME, CStringGetDatum(amname));
- 	if (!OidIsValid(oid) && !missing_ok)
- 		ereport(ERROR,
- 				(errcode(ERRCODE_UNDEFINED_OBJECT),
- 				 errmsg("access method \"%s\" does not exist", amname)));
- 	return oid;
- }
--- 1714,1716 ----
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
new file mode 100644
index df7c2fa..37529e1
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copyCreateTransformStmt(const CreateTra
*** 3828,3833 ****
--- 3828,3845 ----
  	return newnode;
  }
  
+ static CreateAmStmt *
+ _copyCreateAmStmt(const CreateAmStmt *from)
+ {
+ 	CreateAmStmt *newnode = makeNode(CreateAmStmt);
+ 
+ 	COPY_STRING_FIELD(amname);
+ 	COPY_NODE_FIELD(handler_name);
+ 	COPY_SCALAR_FIELD(amtype);
+ 
+ 	return newnode;
+ }
+ 
  static CreateTrigStmt *
  _copyCreateTrigStmt(const CreateTrigStmt *from)
  {
*************** copyObject(const void *from)
*** 4819,4824 ****
--- 4831,4839 ----
  		case T_CreateTransformStmt:
  			retval = _copyCreateTransformStmt(from);
  			break;
+ 		case T_CreateAmStmt:
+ 			retval = _copyCreateAmStmt(from);
+ 			break;
  		case T_CreateTrigStmt:
  			retval = _copyCreateTrigStmt(from);
  			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
new file mode 100644
index b9c3959..f19fde1
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
*************** _equalCreateTransformStmt(const CreateTr
*** 1855,1860 ****
--- 1855,1870 ----
  }
  
  static bool
+ _equalCreateAmStmt(const CreateAmStmt *a, const CreateAmStmt *b)
+ {
+ 	COMPARE_STRING_FIELD(amname);
+ 	COMPARE_NODE_FIELD(handler_name);
+ 	COMPARE_SCALAR_FIELD(amtype);
+ 
+ 	return true;
+ }
+ 
+ static bool
  _equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b)
  {
  	COMPARE_STRING_FIELD(trigname);
*************** equal(const void *a, const void *b)
*** 3146,3151 ****
--- 3156,3164 ----
  		case T_CreateTransformStmt:
  			retval = _equalCreateTransformStmt(a, b);
  			break;
+ 		case T_CreateAmStmt:
+ 			retval = _equalCreateAmStmt(a, b);
+ 			break;
  		case T_CreateTrigStmt:
  			retval = _equalCreateTrigStmt(a, b);
  			break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
new file mode 100644
index b9aeb31..83407ae
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 51,56 ****
--- 51,57 ----
  
  #include "catalog/index.h"
  #include "catalog/namespace.h"
+ #include "catalog/pg_am.h"
  #include "catalog/pg_trigger.h"
  #include "commands/defrem.h"
  #include "commands/trigger.h"
*************** static Node *makeRecursiveViewSelect(cha
*** 263,269 ****
  		DeallocateStmt PrepareStmt ExecuteStmt
  		DropOwnedStmt ReassignOwnedStmt
  		AlterTSConfigurationStmt AlterTSDictionaryStmt
! 		CreateMatViewStmt RefreshMatViewStmt
  
  %type <node>	select_no_parens select_with_parens select_clause
  				simple_select values_clause
--- 264,270 ----
  		DeallocateStmt PrepareStmt ExecuteStmt
  		DropOwnedStmt ReassignOwnedStmt
  		AlterTSConfigurationStmt AlterTSDictionaryStmt
! 		CreateMatViewStmt RefreshMatViewStmt CreateAmStmt DropAmStmt
  
  %type <node>	select_no_parens select_with_parens select_clause
  				simple_select values_clause
*************** static Node *makeRecursiveViewSelect(cha
*** 604,610 ****
  	LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
  	LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
  
! 	MAPPING MATCH MATERIALIZED MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
  
  	NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NONE
  	NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
--- 605,611 ----
  	LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
  	LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
  
! 	MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE
  
  	NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NONE
  	NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
*************** stmt :
*** 789,794 ****
--- 790,796 ----
  			| CommentStmt
  			| ConstraintsSetStmt
  			| CopyStmt
+ 			| CreateAmStmt
  			| CreateAsStmt
  			| CreateAssertStmt
  			| CreateCastStmt
*************** stmt :
*** 823,828 ****
--- 825,831 ----
  			| DeleteStmt
  			| DiscardStmt
  			| DoStmt
+ 			| DropAmStmt
  			| DropAssertStmt
  			| DropCastStmt
  			| DropFdwStmt
*************** row_security_cmd:
*** 4708,4713 ****
--- 4711,4765 ----
  
  /*****************************************************************************
   *
+  *		QUERY:
+  *             CREATE ACCESS METHOD name HANDLER handler_name
+  *
+  *****************************************************************************/
+ 
+ CreateAmStmt: CREATE ACCESS METHOD name TYPE_P INDEX HANDLER handler_name
+ 				{
+ 					CreateAmStmt *n = makeNode(CreateAmStmt);
+ 					n->amname = $4;
+ 					n->handler_name = $8;
+ 					n->amtype = AMTYPE_INDEX;
+ 					$$ = (Node *) n;
+ 				}
+ 		;
+ 
+ /*****************************************************************************
+  *
+  *		QUERY :
+  *				DROP ACCESS METHOD name
+  *
+  ****************************************************************************/
+ 
+ DropAmStmt: DROP ACCESS METHOD name opt_drop_behavior
+ 				{
+ 					DropStmt *n = makeNode(DropStmt);
+ 					n->removeType = OBJECT_ACCESS_METHOD;
+ 					n->objects = list_make1(list_make1(makeString($4)));
+ 					n->arguments = NIL;
+ 					n->missing_ok = false;
+ 					n->behavior = $5;
+ 					n->concurrent = false;
+ 					$$ = (Node *) n;
+ 				}
+ 				|  DROP ACCESS METHOD IF_P EXISTS name opt_drop_behavior
+ 				{
+ 					DropStmt *n = makeNode(DropStmt);
+ 					n->removeType = OBJECT_ACCESS_METHOD;
+ 					n->objects = list_make1(list_make1(makeString($6)));
+ 					n->arguments = NIL;
+ 					n->missing_ok = true;
+ 					n->behavior = $7;
+ 					n->concurrent = false;
+ 					$$ = (Node *) n;
+ 				}
+ 		;
+ 
+ 
+ /*****************************************************************************
+  *
   *		QUERIES :
   *				CREATE TRIGGER ...
   *				DROP TRIGGER ...
*************** unreserved_keyword:
*** 13778,13783 ****
--- 13830,13836 ----
  			| MATCH
  			| MATERIALIZED
  			| MAXVALUE
+ 			| METHOD
  			| MINUTE_P
  			| MINVALUE
  			| MODE
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
new file mode 100644
index dc431c7..92d1baa
*** a/src/backend/parser/parse_utilcmd.c
--- b/src/backend/parser/parse_utilcmd.c
*************** transformIndexConstraint(Constraint *con
*** 1709,1715 ****
  		 * else dump and reload will produce a different index (breaking
  		 * pg_upgrade in particular).
  		 */
! 		if (index_rel->rd_rel->relam != get_am_oid(DEFAULT_INDEX_TYPE, false))
  			ereport(ERROR,
  					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
  					 errmsg("index \"%s\" is not a btree", index_name),
--- 1709,1715 ----
  		 * else dump and reload will produce a different index (breaking
  		 * pg_upgrade in particular).
  		 */
! 		if (index_rel->rd_rel->relam != get_am_oid(DEFAULT_INDEX_TYPE, AMTYPE_INDEX, false))
  			ereport(ERROR,
  					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
  					 errmsg("index \"%s\" is not a btree", index_name),
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
new file mode 100644
index 045f7f0..4d0aac9
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
*************** ProcessUtilitySlow(Node *parsetree,
*** 1520,1525 ****
--- 1520,1529 ----
  				address = ExecSecLabelStmt((SecLabelStmt *) parsetree);
  				break;
  
+ 			case T_CreateAmStmt:
+ 				address = CreateAccessMethod((CreateAmStmt *) parsetree);
+ 				break;
+ 
  			default:
  				elog(ERROR, "unrecognized node type: %d",
  					 (int) nodeTag(parsetree));
*************** CreateCommandTag(Node *parsetree)
*** 2160,2165 ****
--- 2164,2172 ----
  				case OBJECT_TRANSFORM:
  					tag = "DROP TRANSFORM";
  					break;
+ 				case OBJECT_ACCESS_METHOD:
+ 					tag = "DROP ACCESS METHOD";
+ 					break;
  				default:
  					tag = "???";
  			}
*************** CreateCommandTag(Node *parsetree)
*** 2256,2261 ****
--- 2263,2271 ----
  				case OBJECT_COLLATION:
  					tag = "CREATE COLLATION";
  					break;
+ 				case OBJECT_ACCESS_METHOD:
+ 					tag = "CREATE ACCESS METHOD";
+ 					break;
  				default:
  					tag = "???";
  			}
*************** CreateCommandTag(Node *parsetree)
*** 2519,2524 ****
--- 2529,2538 ----
  			tag = "ALTER POLICY";
  			break;
  
+ 		case T_CreateAmStmt:
+ 			tag = "CREATE ACCESS METHOD";
+ 			break;
+ 
  		case T_PrepareStmt:
  			tag = "PREPARE";
  			break;
*************** GetCommandLogLevel(Node *parsetree)
*** 3076,3081 ****
--- 3090,3099 ----
  			lev = LOGSTMT_DDL;
  			break;
  
+ 		case T_CreateAmStmt:
+ 			lev = LOGSTMT_DDL;
+ 			break;
+ 
  			/* already-planned queries */
  		case T_PlannedStmt:
  			{
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
new file mode 100644
index 46c95b0..516af92
*** a/src/backend/utils/adt/selfuncs.c
--- b/src/backend/utils/adt/selfuncs.c
*************** string_to_bytea_const(const char *str, s
*** 6012,6032 ****
   *-------------------------------------------------------------------------
   */
  
! /*
!  * deconstruct_indexquals is a simple function to examine the indexquals
!  * attached to a proposed IndexPath.  It returns a list of IndexQualInfo
!  * structs, one per qual expression.
!  */
! typedef struct
! {
! 	RestrictInfo *rinfo;		/* the indexqual itself */
! 	int			indexcol;		/* zero-based index column number */
! 	bool		varonleft;		/* true if index column is on left of qual */
! 	Oid			clause_op;		/* qual's operator OID, if relevant */
! 	Node	   *other_operand;	/* non-index operand of qual's operator */
! } IndexQualInfo;
! 
! static List *
  deconstruct_indexquals(IndexPath *path)
  {
  	List	   *result = NIL;
--- 6012,6018 ----
   *-------------------------------------------------------------------------
   */
  
! List *
  deconstruct_indexquals(IndexPath *path)
  {
  	List	   *result = NIL;
*************** orderby_operands_eval_cost(PlannerInfo *
*** 6176,6210 ****
  	return qual_arg_cost;
  }
  
! /*
!  * genericcostestimate is a general-purpose estimator that can be used for
!  * most index types.  In some cases we use genericcostestimate as the base
!  * code and then incorporate additional index-type-specific knowledge in
!  * the type-specific calling function.  To avoid code duplication, we make
!  * genericcostestimate return a number of intermediate values as well as
!  * its preliminary estimates of the output cost values.  The GenericCosts
!  * struct includes all these values.
!  *
!  * Callers should initialize all fields of GenericCosts to zero.  In addition,
!  * they can set numIndexTuples to some positive value if they have a better
!  * than default way of estimating the number of leaf index tuples visited.
!  */
! typedef struct
! {
! 	/* These are the values the cost estimator must return to the planner */
! 	Cost		indexStartupCost;		/* index-related startup cost */
! 	Cost		indexTotalCost; /* total index-related scan cost */
! 	Selectivity indexSelectivity;		/* selectivity of index */
! 	double		indexCorrelation;		/* order correlation of index */
! 
! 	/* Intermediate values we obtain along the way */
! 	double		numIndexPages;	/* number of leaf pages visited */
! 	double		numIndexTuples; /* number of leaf tuples visited */
! 	double		spc_random_page_cost;	/* relevant random_page_cost value */
! 	double		num_sa_scans;	/* # indexscans from ScalarArrayOps */
! } GenericCosts;
! 
! static void
  genericcostestimate(PlannerInfo *root,
  					IndexPath *path,
  					double loop_count,
--- 6162,6168 ----
  	return qual_arg_cost;
  }
  
! void
  genericcostestimate(PlannerInfo *root,
  					IndexPath *path,
  					double loop_count,
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
new file mode 100644
index f798b15..1acd91a
*** a/src/bin/pg_dump/common.c
--- b/src/bin/pg_dump/common.c
*************** getSchemaData(Archive *fout, int *numTab
*** 98,103 ****
--- 98,104 ----
  	int			numProcLangs;
  	int			numCasts;
  	int			numTransforms;
+ 	int			numAccessMethods;
  	int			numOpclasses;
  	int			numOpfamilies;
  	int			numConversions;
*************** getSchemaData(Archive *fout, int *numTab
*** 169,174 ****
--- 170,179 ----
  	oprinfoindex = buildIndexArray(oprinfo, numOperators, sizeof(OprInfo));
  
  	if (g_verbose)
+ 		write_msg(NULL, "reading user-defined access methods\n");
+ 	getAccessMethods(fout, &numAccessMethods);
+ 
+ 	if (g_verbose)
  		write_msg(NULL, "reading user-defined operator classes\n");
  	getOpclasses(fout, &numOpclasses);
  
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
new file mode 100644
index 64c2673..9061afa
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 45,50 ****
--- 45,51 ----
  #include "access/attnum.h"
  #include "access/sysattr.h"
  #include "access/transam.h"
+ #include "catalog/pg_am.h"
  #include "catalog/pg_cast.h"
  #include "catalog/pg_class.h"
  #include "catalog/pg_default_acl.h"
*************** static void dumpFunc(Archive *fout, Func
*** 173,178 ****
--- 174,180 ----
  static void dumpCast(Archive *fout, CastInfo *cast);
  static void dumpTransform(Archive *fout, TransformInfo *transform);
  static void dumpOpr(Archive *fout, OprInfo *oprinfo);
+ static void dumpAccessMethod(Archive *fout, AccessMethodInfo *oprinfo);
  static void dumpOpclass(Archive *fout, OpclassInfo *opcinfo);
  static void dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo);
  static void dumpCollation(Archive *fout, CollInfo *convinfo);
*************** getConversions(Archive *fout, int *numCo
*** 4101,4106 ****
--- 4103,4187 ----
  }
  
  /*
+  * getAccessMethods:
+  *	  read all user-defined access methods in the system catalogs and return
+  *    them in the AccessMethodInfo* structure
+  *
+  *	numAccessMethods is set to the number of access methods read in
+  */
+ AccessMethodInfo *
+ getAccessMethods(Archive *fout, int *numAccessMethods)
+ {
+ 	DumpOptions *dopt = fout->dopt;
+ 	PGresult   *res;
+ 	int			ntups;
+ 	int			i;
+ 	PQExpBuffer query;
+ 	AccessMethodInfo *aminfo;
+ 	int			i_tableoid;
+ 	int			i_oid;
+ 	int			i_amname;
+ 	int			i_amhandler;
+ 	int			i_amtype;
+ 
+ 	/* Before 9.6, there are no user-defined access methods */
+ 	if (fout->remoteVersion < 90600)
+ 	{
+ 		*numAccessMethods = 0;
+ 		return NULL;
+ 	}
+ 
+ 	query = createPQExpBuffer();
+ 
+ 	/* Make sure we are in proper schema */
+ 	selectSourceSchema(fout, "pg_catalog");
+ 
+ 	/*
+ 	 * Select only user-defined access methods assuming all built-in access
+ 	 * methods have oid < 10000.
+ 	 */
+ 	appendPQExpBuffer(query, "SELECT tableoid, oid, amname, amtype, "
+ 					  "amhandler::pg_catalog.regproc AS amhandler "
+ 					  "FROM pg_am "
+ 					  "WHERE oid >= 10000");
+ 
+ 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+ 
+ 	ntups = PQntuples(res);
+ 	*numAccessMethods = ntups;
+ 
+ 	aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
+ 
+ 	i_tableoid = PQfnumber(res, "tableoid");
+ 	i_oid = PQfnumber(res, "oid");
+ 	i_amname = PQfnumber(res, "amname");
+ 	i_amhandler = PQfnumber(res, "amhandler");
+ 	i_amtype = PQfnumber(res, "amtype");
+ 
+ 	for (i = 0; i < ntups; i++)
+ 	{
+ 		aminfo[i].dobj.objType = DO_ACCESS_METHOD;
+ 		aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ 		aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ 		AssignDumpId(&aminfo[i].dobj);
+ 		aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
+ 		aminfo[i].dobj.namespace = NULL;
+ 		aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
+ 		aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
+ 
+ 		/* Decide whether we want to dump it */
+ 		selectDumpableObject(&(aminfo[i].dobj), dopt);
+ 	}
+ 
+ 	PQclear(res);
+ 
+ 	destroyPQExpBuffer(query);
+ 
+ 	return aminfo;
+ }
+ 
+ 
+ /*
   * getOpclasses:
   *	  read all opclasses in the system catalogs and return them in the
   * OpclassInfo* structure
*************** dumpDumpableObject(Archive *fout, Dumpab
*** 8408,8413 ****
--- 8489,8497 ----
  		case DO_OPERATOR:
  			dumpOpr(fout, (OprInfo *) dobj);
  			break;
+ 		case DO_ACCESS_METHOD:
+ 			dumpAccessMethod(fout, (AccessMethodInfo *) dobj);
+ 			break;
  		case DO_OPCLASS:
  			dumpOpclass(fout, (OpclassInfo *) dobj);
  			break;
*************** convertTSFunction(Archive *fout, Oid fun
*** 11446,11451 ****
--- 11530,11603 ----
  	return result;
  }
  
+ /*
+  * dumpAccessMethod
+  *	  write out a single access method definition
+  */
+ static void
+ dumpAccessMethod(Archive *fout, AccessMethodInfo *aminfo)
+ {
+ 	DumpOptions *dopt = fout->dopt;
+ 	PQExpBuffer q;
+ 	PQExpBuffer delq;
+ 	PQExpBuffer labelq;
+ 	char	   *qamname;
+ 
+ 	/* Skip if not to be dumped */
+ 	if (!aminfo->dobj.dump || dopt->dataOnly)
+ 		return;
+ 
+ 	q = createPQExpBuffer();
+ 	delq = createPQExpBuffer();
+ 	labelq = createPQExpBuffer();
+ 
+ 	qamname = pg_strdup(fmtId(aminfo->dobj.name));
+ 
+ 	appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
+ 
+ 	switch (aminfo->amtype)
+ 	{
+ 		case AMTYPE_INDEX:
+ 			appendPQExpBuffer(q, "TYPE INDEX ");
+ 			break;
+ 		default:
+ 			write_msg(NULL, "WARNING: invalid type %c of access method %s\n",
+ 					  aminfo->amtype, qamname);
+ 			destroyPQExpBuffer(q);
+ 			destroyPQExpBuffer(delq);
+ 			destroyPQExpBuffer(labelq);
+ 			return;
+ 	}
+ 
+ 	appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
+ 
+ 	appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
+ 					  qamname);
+ 
+ 	appendPQExpBuffer(labelq, "ACCESS METHOD %s",
+ 					  qamname);
+ 
+ 	ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
+ 				 aminfo->dobj.name,
+ 				 NULL,
+ 				 NULL,
+ 				 "",
+ 				 false, "ACCESS METHOD", SECTION_PRE_DATA,
+ 				 q->data, delq->data, NULL,
+ 				 NULL, 0,
+ 				 NULL, NULL);
+ 
+ 	/* Dump Access Method Comments */
+ 	dumpComment(fout, labelq->data,
+ 				NULL, "",
+ 				aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
+ 
+ 	free(qamname);
+ 
+ 	destroyPQExpBuffer(q);
+ 	destroyPQExpBuffer(delq);
+ 	destroyPQExpBuffer(labelq);
+ }
  
  /*
   * dumpOpclass
*************** addBoundaryDependencies(DumpableObject *
*** 16227,16232 ****
--- 16379,16385 ----
  			case DO_FUNC:
  			case DO_AGG:
  			case DO_OPERATOR:
+ 			case DO_ACCESS_METHOD:
  			case DO_OPCLASS:
  			case DO_OPFAMILY:
  			case DO_COLLATION:
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
new file mode 100644
index 9a1d8f8..66e6931
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
*************** typedef enum
*** 48,53 ****
--- 48,54 ----
  	DO_FUNC,
  	DO_AGG,
  	DO_OPERATOR,
+ 	DO_ACCESS_METHOD,
  	DO_OPCLASS,
  	DO_OPFAMILY,
  	DO_COLLATION,
*************** typedef struct _oprInfo
*** 167,172 ****
--- 168,180 ----
  	Oid			oprcode;
  } OprInfo;
  
+ typedef struct _accessMethodInfo
+ {
+ 	DumpableObject dobj;
+ 	char		amtype;
+ 	char	   *amhandler;
+ } AccessMethodInfo;
+ 
  typedef struct _opclassInfo
  {
  	DumpableObject dobj;
*************** extern TypeInfo *getTypes(Archive *fout,
*** 548,553 ****
--- 556,562 ----
  extern FuncInfo *getFuncs(Archive *fout, int *numFuncs);
  extern AggInfo *getAggregates(Archive *fout, int *numAggregates);
  extern OprInfo *getOperators(Archive *fout, int *numOperators);
+ extern AccessMethodInfo *getAccessMethods(Archive *fout, int *numAccessMethods);
  extern OpclassInfo *getOpclasses(Archive *fout, int *numOpclasses);
  extern OpfamilyInfo *getOpfamilies(Archive *fout, int *numOpfamilies);
  extern CollInfo *getCollations(Archive *fout, int *numCollations);
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
new file mode 100644
index 78ff59c..52d5625
*** a/src/bin/pg_dump/pg_dump_sort.c
--- b/src/bin/pg_dump/pg_dump_sort.c
*************** static const int oldObjectTypePriority[]
*** 45,50 ****
--- 45,51 ----
  	2,							/* DO_FUNC */
  	3,							/* DO_AGG */
  	3,							/* DO_OPERATOR */
+ 	3,							/* DO_ACCESS_METHOD */
  	4,							/* DO_OPCLASS */
  	4,							/* DO_OPFAMILY */
  	4,							/* DO_COLLATION */
*************** static const int newObjectTypePriority[]
*** 95,100 ****
--- 96,102 ----
  	6,							/* DO_FUNC */
  	7,							/* DO_AGG */
  	8,							/* DO_OPERATOR */
+ 	8,							/* DO_ACCESS_METHOD */
  	9,							/* DO_OPCLASS */
  	9,							/* DO_OPFAMILY */
  	3,							/* DO_COLLATION */
*************** describeDumpableObject(DumpableObject *o
*** 1329,1334 ****
--- 1331,1341 ----
  					 "OPERATOR %s  (ID %d OID %u)",
  					 obj->name, obj->dumpId, obj->catId.oid);
  			return;
+ 		case DO_ACCESS_METHOD:
+ 			snprintf(buf, bufsize,
+ 					 "ACCESS METHOD %s  (ID %d OID %u)",
+ 					 obj->name, obj->dumpId, obj->catId.oid);
+ 			return;
  		case DO_OPCLASS:
  			snprintf(buf, bufsize,
  					 "OPERATOR CLASS %s  (ID %d OID %u)",
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
new file mode 100644
index 049bf9f..ac16740
*** a/src/include/catalog/dependency.h
--- b/src/include/catalog/dependency.h
*************** typedef enum ObjectClass
*** 153,162 ****
  	OCLASS_EXTENSION,			/* pg_extension */
  	OCLASS_EVENT_TRIGGER,		/* pg_event_trigger */
  	OCLASS_POLICY,				/* pg_policy */
! 	OCLASS_TRANSFORM			/* pg_transform */
  } ObjectClass;
  
! #define LAST_OCLASS		OCLASS_TRANSFORM
  
  
  /* in dependency.c */
--- 153,163 ----
  	OCLASS_EXTENSION,			/* pg_extension */
  	OCLASS_EVENT_TRIGGER,		/* pg_event_trigger */
  	OCLASS_POLICY,				/* pg_policy */
! 	OCLASS_TRANSFORM,			/* pg_transform */
! 	OCLASS_AM,					/* pg_am */
  } ObjectClass;
  
! #define LAST_OCLASS		OCLASS_AM
  
  
  /* in dependency.c */
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
new file mode 100644
index f801c3e..6fba1cd
*** a/src/include/catalog/pg_am.h
--- b/src/include/catalog/pg_am.h
*************** CATALOG(pg_am,2601)
*** 35,40 ****
--- 35,41 ----
  {
  	NameData	amname;			/* access method name */
  	regproc		amhandler;		/* handler function */
+ 	char		amtype;			/* see AMTYPE_xxx constants below */
  } FormData_pg_am;
  
  /* ----------------
*************** typedef FormData_pg_am *Form_pg_am;
*** 48,78 ****
   *		compiler constants for pg_am
   * ----------------
   */
! #define Natts_pg_am						2
  #define Anum_pg_am_amname				1
  #define Anum_pg_am_amhandler			2
  
  /* ----------------
   *		initial contents of pg_am
   * ----------------
   */
  
! DATA(insert OID = 403 (  btree		bthandler ));
  DESCR("b-tree index access method");
  #define BTREE_AM_OID 403
! DATA(insert OID = 405 (  hash		hashhandler ));
  DESCR("hash index access method");
  #define HASH_AM_OID 405
! DATA(insert OID = 783 (  gist		gisthandler ));
  DESCR("GiST index access method");
  #define GIST_AM_OID 783
! DATA(insert OID = 2742 (  gin		ginhandler ));
  DESCR("GIN index access method");
  #define GIN_AM_OID 2742
! DATA(insert OID = 4000 (  spgist	spghandler ));
  DESCR("SP-GiST index access method");
  #define SPGIST_AM_OID 4000
! DATA(insert OID = 3580 (  brin		brinhandler ));
  DESCR("block range index (BRIN) access method");
  #define BRIN_AM_OID 3580
  
--- 49,86 ----
   *		compiler constants for pg_am
   * ----------------
   */
! #define Natts_pg_am						3
  #define Anum_pg_am_amname				1
  #define Anum_pg_am_amhandler			2
+ #define Anum_pg_am_amtype				3
+ 
+ /* ----------------
+  *		compiler constant for amtype
+  * ----------------
+  */
+ #define AMTYPE_INDEX					'i'		/* index access method */
  
  /* ----------------
   *		initial contents of pg_am
   * ----------------
   */
  
! DATA(insert OID = 403 (  btree		bthandler	i ));
  DESCR("b-tree index access method");
  #define BTREE_AM_OID 403
! DATA(insert OID = 405 (  hash		hashhandler	i ));
  DESCR("hash index access method");
  #define HASH_AM_OID 405
! DATA(insert OID = 783 (  gist		gisthandler	i ));
  DESCR("GiST index access method");
  #define GIST_AM_OID 783
! DATA(insert OID = 2742 (  gin		ginhandler	i ));
  DESCR("GIN index access method");
  #define GIN_AM_OID 2742
! DATA(insert OID = 4000 (  spgist	spghandler	i ));
  DESCR("SP-GiST index access method");
  #define SPGIST_AM_OID 4000
! DATA(insert OID = 3580 (  brin		brinhandler	i ));
  DESCR("block range index (BRIN) access method");
  #define BRIN_AM_OID 3580
  
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
new file mode 100644
index 54f67e9..1e21f03
*** a/src/include/commands/defrem.h
--- b/src/include/commands/defrem.h
*************** extern void IsThereOpClassInNamespace(co
*** 91,98 ****
  						  Oid opcnamespace);
  extern void IsThereOpFamilyInNamespace(const char *opfname, Oid opfmethod,
  						   Oid opfnamespace);
- extern Oid	get_am_oid(const char *amname, bool missing_ok);
- extern char *get_am_name(Oid amOid);
  extern Oid	get_opclass_oid(Oid amID, List *opclassname, bool missing_ok);
  extern Oid	get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok);
  
--- 91,96 ----
*************** extern Datum transformGenericOptions(Oid
*** 137,142 ****
--- 135,146 ----
  						List *options,
  						Oid fdwvalidator);
  
+ /* commands/amcmds.c */
+ extern ObjectAddress CreateAccessMethod(CreateAmStmt *stmt);
+ extern void RemoveAccessMethodById(Oid amOid);
+ extern Oid	get_am_oid(const char *amname, char amtype, bool missing_ok);
+ extern char *get_am_name(Oid amOid);
+ 
  /* support routines in commands/define.c */
  
  extern char *defGetString(DefElem *def);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
new file mode 100644
index fad9988..8cd1d89
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
*************** typedef enum NodeTag
*** 401,406 ****
--- 401,407 ----
  	T_CreatePolicyStmt,
  	T_AlterPolicyStmt,
  	T_CreateTransformStmt,
+ 	T_CreateAmStmt,
  
  	/*
  	 * TAGS FOR PARSE TREE NODES (parsenodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
new file mode 100644
index 2fd0629..8b958b4
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
*************** typedef struct SetOperationStmt
*** 1379,1384 ****
--- 1379,1385 ----
  
  typedef enum ObjectType
  {
+ 	OBJECT_ACCESS_METHOD,
  	OBJECT_AGGREGATE,
  	OBJECT_AMOP,
  	OBJECT_AMPROC,
*************** typedef struct AlterPolicyStmt
*** 2070,2075 ****
--- 2071,2088 ----
  	Node	   *with_check;		/* the policy's WITH CHECK condition. */
  } AlterPolicyStmt;
  
+ /*----------------------
+  *		Create ACCESS METHOD Statement
+  *----------------------
+  */
+ typedef struct CreateAmStmt
+ {
+ 	NodeTag		type;
+ 	char	   *amname;			/* access method name */
+ 	List	   *handler_name;	/* handler function name */
+ 	char		amtype;			/* type of access method */
+ } CreateAmStmt;
+ 
  /* ----------------------
   *		Create TRIGGER Statement
   * ----------------------
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
new file mode 100644
index 6e1e820..7de3404
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
*************** PG_KEYWORD("mapping", MAPPING, UNRESERVE
*** 239,244 ****
--- 239,245 ----
  PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD)
  PG_KEYWORD("materialized", MATERIALIZED, UNRESERVED_KEYWORD)
  PG_KEYWORD("maxvalue", MAXVALUE, UNRESERVED_KEYWORD)
+ PG_KEYWORD("method", METHOD, UNRESERVED_KEYWORD)
  PG_KEYWORD("minute", MINUTE_P, UNRESERVED_KEYWORD)
  PG_KEYWORD("minvalue", MINVALUE, UNRESERVED_KEYWORD)
  PG_KEYWORD("mode", MODE, UNRESERVED_KEYWORD)
diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h
new file mode 100644
index 06fbca7..7fb7466
*** a/src/include/utils/selfuncs.h
--- b/src/include/utils/selfuncs.h
*************** typedef enum
*** 95,100 ****
--- 95,142 ----
  	Pattern_Prefix_None, Pattern_Prefix_Partial, Pattern_Prefix_Exact
  } Pattern_Prefix_Status;
  
+ /*
+  * deconstruct_indexquals is a simple function to examine the indexquals
+  * attached to a proposed IndexPath.  It returns a list of IndexQualInfo
+  * structs, one per qual expression.
+  */
+ typedef struct
+ {
+ 	RestrictInfo *rinfo;		/* the indexqual itself */
+ 	int			indexcol;		/* zero-based index column number */
+ 	bool		varonleft;		/* true if index column is on left of qual */
+ 	Oid			clause_op;		/* qual's operator OID, if relevant */
+ 	Node	   *other_operand;	/* non-index operand of qual's operator */
+ } IndexQualInfo;
+ 
+ /*
+  * genericcostestimate is a general-purpose estimator that can be used for
+  * most index types.  In some cases we use genericcostestimate as the base
+  * code and then incorporate additional index-type-specific knowledge in
+  * the type-specific calling function.  To avoid code duplication, we make
+  * genericcostestimate return a number of intermediate values as well as
+  * its preliminary estimates of the output cost values.  The GenericCosts
+  * struct includes all these values.
+  *
+  * Callers should initialize all fields of GenericCosts to zero.  In addition,
+  * they can set numIndexTuples to some positive value if they have a better
+  * than default way of estimating the number of leaf index tuples visited.
+  */
+ typedef struct
+ {
+ 	/* These are the values the cost estimator must return to the planner */
+ 	Cost		indexStartupCost;		/* index-related startup cost */
+ 	Cost		indexTotalCost; /* total index-related scan cost */
+ 	Selectivity indexSelectivity;		/* selectivity of index */
+ 	double		indexCorrelation;		/* order correlation of index */
+ 
+ 	/* Intermediate values we obtain along the way */
+ 	double		numIndexPages;	/* number of leaf pages visited */
+ 	double		numIndexTuples; /* number of leaf tuples visited */
+ 	double		spc_random_page_cost;	/* relevant random_page_cost value */
+ 	double		num_sa_scans;	/* # indexscans from ScalarArrayOps */
+ } GenericCosts;
+ 
  /* Hooks for plugins to get control when we ask for stats */
  typedef bool (*get_relation_stats_hook_type) (PlannerInfo *root,
  														  RangeTblEntry *rte,
*************** extern double estimate_num_groups(Planne
*** 191,196 ****
--- 233,244 ----
  extern Selectivity estimate_hash_bucketsize(PlannerInfo *root, Node *hashkey,
  						 double nbuckets);
  
+ extern List *deconstruct_indexquals(IndexPath *path);
+ extern void genericcostestimate(PlannerInfo *root, IndexPath *path,
+ 								double loop_count,
+ 								List *qinfos,
+ 								GenericCosts *costs);
+ 
  /* Functions in array_selfuncs.c */
  
  extern Selectivity scalararraysel_containment(PlannerInfo *root,
diff --git a/src/test/regress/expected/create_am.out b/src/test/regress/expected/create_am.out
new file mode 100644
index ...47d6024
*** a/src/test/regress/expected/create_am.out
--- b/src/test/regress/expected/create_am.out
***************
*** 0 ****
--- 1,108 ----
+ --
+ -- Create access method tests
+ --
+ -- Make gist2 over gisthandler. In fact, it would be a synonym to gist.
+ CREATE ACCESS METHOD gist2 TYPE INDEX HANDLER gisthandler;
+ -- Drop old index on fast_emp4000
+ DROP INDEX grect2ind;
+ -- Try to create gist2 index on fast_emp4000: fail because opclass doesn't exist
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ ERROR:  data type box has no default operator class for access method "gist2"
+ HINT:  You must specify an operator class for the index or define a default operator class for the data type.
+ -- Make operator class for boxes using gist2
+ CREATE OPERATOR CLASS box_ops DEFAULT
+ 	FOR TYPE box USING gist2 AS
+ 	OPERATOR 1	<<,
+ 	OPERATOR 2	&<,
+ 	OPERATOR 3	&&,
+ 	OPERATOR 4	&>,
+ 	OPERATOR 5	>>,
+ 	OPERATOR 6	~=,
+ 	OPERATOR 7	@>,
+ 	OPERATOR 8	<@,
+ 	OPERATOR 9	&<|,
+ 	OPERATOR 10	<<|,
+ 	OPERATOR 11	|>>,
+ 	OPERATOR 12	|&>,
+ 	OPERATOR 13	~,
+ 	OPERATOR 14	@,
+ 	FUNCTION 1	gist_box_consistent(internal, box, smallint, oid, internal),
+ 	FUNCTION 2	gist_box_union(internal, internal),
+ 	FUNCTION 3	gist_box_compress(internal),
+ 	FUNCTION 4	gist_box_decompress(internal),
+ 	FUNCTION 5	gist_box_penalty(internal, internal, internal),
+ 	FUNCTION 6	gist_box_picksplit(internal, internal),
+ 	FUNCTION 7	gist_box_same(box, box, internal),
+ 	FUNCTION 9	gist_box_fetch(internal);
+ -- Create gist2 index on fast_emp4000
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ -- Now check the results from plain indexscan
+ SET enable_seqscan = OFF;
+ SET enable_indexscan = ON;
+ SET enable_bitmapscan = OFF;
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+                            QUERY PLAN                           
+ ----------------------------------------------------------------
+  Sort
+    Sort Key: ((home_base[0])[0])
+    ->  Index Only Scan using grect2ind on fast_emp4000
+          Index Cond: (home_base @ '(2000,1000),(200,200)'::box)
+ (4 rows)
+ 
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+        home_base       
+ -----------------------
+  (337,455),(240,359)
+  (1444,403),(1346,344)
+ (2 rows)
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+                          QUERY PLAN                          
+ -------------------------------------------------------------
+  Aggregate
+    ->  Index Only Scan using grect2ind on fast_emp4000
+          Index Cond: (home_base && '(1000,1000),(0,0)'::box)
+ (3 rows)
+ 
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+  count 
+ -------
+      2
+ (1 row)
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+                       QUERY PLAN                       
+ -------------------------------------------------------
+  Aggregate
+    ->  Index Only Scan using grect2ind on fast_emp4000
+          Index Cond: (home_base IS NULL)
+ (3 rows)
+ 
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+  count 
+ -------
+    278
+ (1 row)
+ 
+ -- Try to drop access method: fail because of depending objects
+ DROP ACCESS METHOD gist2;
+ ERROR:  cannot drop access method gist2 because other objects depend on it
+ DETAIL:  operator class box_ops for access method gist2 depends on access method gist2
+ index grect2ind depends on operator class box_ops for access method gist2
+ HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+ -- Drop access method cascade
+ DROP ACCESS METHOD gist2 CASCADE;
+ NOTICE:  drop cascades to 2 other objects
+ DETAIL:  drop cascades to operator class box_ops for access method gist2
+ drop cascades to index grect2ind
+ -- Reset optimizer options
+ RESET enable_seqscan;
+ RESET enable_indexscan;
+ RESET enable_bitmapscan;
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
new file mode 100644
index 75751be..2330a19
*** a/src/test/regress/expected/object_address.out
--- b/src/test/regress/expected/object_address.out
*************** BEGIN
*** 80,86 ****
  		('text search parser'), ('text search dictionary'),
  		('text search template'), ('text search configuration'),
  		('policy'), ('user mapping'), ('default acl'), ('transform'),
! 		('operator of access method'), ('function of access method')
  	LOOP
  		FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}')
  		LOOP
--- 80,87 ----
  		('text search parser'), ('text search dictionary'),
  		('text search template'), ('text search configuration'),
  		('policy'), ('user mapping'), ('default acl'), ('transform'),
! 		('operator of access method'), ('function of access method'),
! 		('access method')
  	LOOP
  		FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}')
  		LOOP
*************** WARNING:  error for function of access m
*** 282,287 ****
--- 283,294 ----
  WARNING:  error for function of access method,{addr_nsp,zwei},{integer}: name list length must be at least 3
  WARNING:  error for function of access method,{eins,zwei,drei},{}: argument list length must be exactly 2
  WARNING:  error for function of access method,{eins,zwei,drei},{integer}: argument list length must be exactly 2
+ WARNING:  error for access method,{eins},{}: access method "eins" does not exist
+ WARNING:  error for access method,{eins},{integer}: access method "eins" does not exist
+ WARNING:  error for access method,{addr_nsp,zwei},{}: cross-database references are not implemented: addr_nsp.zwei
+ WARNING:  error for access method,{addr_nsp,zwei},{integer}: cross-database references are not implemented: addr_nsp.zwei
+ WARNING:  error for access method,{eins,zwei,drei},{}: improper access method name (too many dotted names): eins.zwei.drei
+ WARNING:  error for access method,{eins,zwei,drei},{integer}: improper access method name (too many dotted names): eins.zwei.drei
  -- these object types cannot be qualified names
  SELECT pg_get_object_address('language', '{one}', '{}');
  ERROR:  language "one" does not exist
*************** WITH objects (type, name, args) AS (VALU
*** 373,379 ****
  				-- extension
  				-- event trigger
  				('policy', '{addr_nsp, gentable, genpol}', '{}'),
! 				('transform', '{int}', '{sql}')
          )
  SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*,
  	-- test roundtrip through pg_identify_object_as_address
--- 380,387 ----
  				-- extension
  				-- event trigger
  				('policy', '{addr_nsp, gentable, genpol}', '{}'),
! 				('transform', '{int}', '{sql}'),
! 				('access method', '{btree}', '{}')
          )
  SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*,
  	-- test roundtrip through pg_identify_object_as_address
*************** SELECT (pg_identify_object(addr1.classid
*** 405,410 ****
--- 413,419 ----
   server                    |            | addr_fserv        | addr_fserv                                                           | t
   user mapping              |            |                   | regtest_addr_user on server integer                                  | t
   foreign-data wrapper      |            | addr_fdw          | addr_fdw                                                             | t
+  access method             |            | btree             | btree                                                                | t
   operator of access method |            |                   | operator 1 (integer, integer) of pg_catalog.integer_ops USING btree  | t
   function of access method |            |                   | function 2 (integer, integer) of pg_catalog.integer_ops USING btree  | t
   default value             |            |                   | for addr_nsp.gentable.b                                              | t
*************** SELECT (pg_identify_object(addr1.classid
*** 426,432 ****
   text search parser        | addr_nsp   | addr_ts_prs       | addr_nsp.addr_ts_prs                                                 | t
   text search configuration | addr_nsp   | addr_ts_conf      | addr_nsp.addr_ts_conf                                                | t
   text search template      | addr_nsp   | addr_ts_temp      | addr_nsp.addr_ts_temp                                                | t
! (41 rows)
  
  ---
  --- Cleanup resources
--- 435,441 ----
   text search parser        | addr_nsp   | addr_ts_prs       | addr_nsp.addr_ts_prs                                                 | t
   text search configuration | addr_nsp   | addr_ts_conf      | addr_nsp.addr_ts_conf                                                | t
   text search template      | addr_nsp   | addr_ts_temp      | addr_nsp.addr_ts_temp                                                | t
! (42 rows)
  
  ---
  --- Cleanup resources
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
new file mode 100644
index eb0bc88..2c5be4b
*** a/src/test/regress/expected/sanity_check.out
--- b/src/test/regress/expected/sanity_check.out
*************** e_star|f
*** 44,50 ****
  emp|f
  equipment_r|f
  f_star|f
! fast_emp4000|t
  float4_tbl|f
  float8_tbl|f
  func_index_heap|t
--- 44,50 ----
  emp|f
  equipment_r|f
  f_star|f
! fast_emp4000|f
  float4_tbl|f
  float8_tbl|f
  func_index_heap|t
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
new file mode 100644
index bec0316..8be4b83
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
*************** test: create_index create_view
*** 60,66 ****
  # ----------
  # Another group of parallel tests
  # ----------
! test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes
  
  # ----------
  # sanity_check does a vacuum, affecting the sort order of SELECT *
--- 60,66 ----
  # ----------
  # Another group of parallel tests
  # ----------
! test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes create_am
  
  # ----------
  # sanity_check does a vacuum, affecting the sort order of SELECT *
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
new file mode 100644
index 7e9b319..1de3da8
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
*************** test: drop_if_exists
*** 75,80 ****
--- 75,81 ----
  test: updatable_views
  test: rolenames
  test: roleattributes
+ test: create_am
  test: sanity_check
  test: errors
  test: select
diff --git a/src/test/regress/sql/create_am.sql b/src/test/regress/sql/create_am.sql
new file mode 100644
index ...e2051c5
*** a/src/test/regress/sql/create_am.sql
--- b/src/test/regress/sql/create_am.sql
***************
*** 0 ****
--- 1,73 ----
+ --
+ -- Create access method tests
+ --
+ 
+ -- Make gist2 over gisthandler. In fact, it would be a synonym to gist.
+ CREATE ACCESS METHOD gist2 TYPE INDEX HANDLER gisthandler;
+ 
+ -- Drop old index on fast_emp4000
+ DROP INDEX grect2ind;
+ 
+ -- Try to create gist2 index on fast_emp4000: fail because opclass doesn't exist
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ 
+ -- Make operator class for boxes using gist2
+ CREATE OPERATOR CLASS box_ops DEFAULT
+ 	FOR TYPE box USING gist2 AS
+ 	OPERATOR 1	<<,
+ 	OPERATOR 2	&<,
+ 	OPERATOR 3	&&,
+ 	OPERATOR 4	&>,
+ 	OPERATOR 5	>>,
+ 	OPERATOR 6	~=,
+ 	OPERATOR 7	@>,
+ 	OPERATOR 8	<@,
+ 	OPERATOR 9	&<|,
+ 	OPERATOR 10	<<|,
+ 	OPERATOR 11	|>>,
+ 	OPERATOR 12	|&>,
+ 	OPERATOR 13	~,
+ 	OPERATOR 14	@,
+ 	FUNCTION 1	gist_box_consistent(internal, box, smallint, oid, internal),
+ 	FUNCTION 2	gist_box_union(internal, internal),
+ 	FUNCTION 3	gist_box_compress(internal),
+ 	FUNCTION 4	gist_box_decompress(internal),
+ 	FUNCTION 5	gist_box_penalty(internal, internal, internal),
+ 	FUNCTION 6	gist_box_picksplit(internal, internal),
+ 	FUNCTION 7	gist_box_same(box, box, internal),
+ 	FUNCTION 9	gist_box_fetch(internal);
+ 
+ -- Create gist2 index on fast_emp4000
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ 
+ -- Now check the results from plain indexscan
+ SET enable_seqscan = OFF;
+ SET enable_indexscan = ON;
+ SET enable_bitmapscan = OFF;
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+ 
+ -- Try to drop access method: fail because of depending objects
+ DROP ACCESS METHOD gist2;
+ 
+ -- Drop access method cascade
+ DROP ACCESS METHOD gist2 CASCADE;
+ 
+ -- Reset optimizer options
+ RESET enable_seqscan;
+ RESET enable_indexscan;
+ RESET enable_bitmapscan;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
new file mode 100644
index 68e7cb0..1b9eb2d
*** a/src/test/regress/sql/object_address.sql
--- b/src/test/regress/sql/object_address.sql
*************** BEGIN
*** 78,84 ****
  		('text search parser'), ('text search dictionary'),
  		('text search template'), ('text search configuration'),
  		('policy'), ('user mapping'), ('default acl'), ('transform'),
! 		('operator of access method'), ('function of access method')
  	LOOP
  		FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}')
  		LOOP
--- 78,85 ----
  		('text search parser'), ('text search dictionary'),
  		('text search template'), ('text search configuration'),
  		('policy'), ('user mapping'), ('default acl'), ('transform'),
! 		('operator of access method'), ('function of access method'),
! 		('access method')
  	LOOP
  		FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}')
  		LOOP
*************** WITH objects (type, name, args) AS (VALU
*** 166,172 ****
  				-- extension
  				-- event trigger
  				('policy', '{addr_nsp, gentable, genpol}', '{}'),
! 				('transform', '{int}', '{sql}')
          )
  SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*,
  	-- test roundtrip through pg_identify_object_as_address
--- 167,174 ----
  				-- extension
  				-- event trigger
  				('policy', '{addr_nsp, gentable, genpol}', '{}'),
! 				('transform', '{int}', '{sql}'),
! 				('access method', '{btree}', '{}')
          )
  SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*,
  	-- test roundtrip through pg_identify_object_as_address
0002-generic-xlog.11.patchapplication/octet-stream; name=0002-generic-xlog.11.patchDownload
diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
new file mode 100644
index c72a1f2..c0e38fd
*** a/src/backend/access/rmgrdesc/Makefile
--- b/src/backend/access/rmgrdesc/Makefile
*************** subdir = src/backend/access/rmgrdesc
*** 8,16 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o gindesc.o gistdesc.o \
! 	   hashdesc.o heapdesc.o mxactdesc.o nbtdesc.o relmapdesc.o \
! 	   replorigindesc.o seqdesc.o smgrdesc.o spgdesc.o \
  	   standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
  
  include $(top_srcdir)/src/backend/common.mk
--- 8,16 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o genericdesc.o \
! 	   gindesc.o gistdesc.o hashdesc.o heapdesc.o mxactdesc.o nbtdesc.o \
! 	   relmapdesc.o replorigindesc.o seqdesc.o smgrdesc.o spgdesc.o \
  	   standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
  
  include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/genericdesc.c b/src/backend/access/rmgrdesc/genericdesc.c
new file mode 100644
index ...3d035c2
*** a/src/backend/access/rmgrdesc/genericdesc.c
--- b/src/backend/access/rmgrdesc/genericdesc.c
***************
*** 0 ****
--- 1,58 ----
+ /*-------------------------------------------------------------------------
+  *
+  * genericdesc.c
+  *	  rmgr descriptor routines for access/transam/generic_xlog.c
+  *
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/backend/access/rmgrdesc/genericdesc.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/generic_xlog.h"
+ #include "lib/stringinfo.h"
+ #include "storage/relfilenode.h"
+ 
+ /*
+  * Description of generic xlog record: write page regions which this record
+  * overrides.
+  */
+ void
+ generic_desc(StringInfo buf, XLogReaderState *record)
+ {
+ 	Pointer		ptr = XLogRecGetData(record),
+ 				end = ptr + XLogRecGetDataLen(record);
+ 
+ 	while (ptr < end)
+ 	{
+ 		OffsetNumber	offset,
+ 						length;
+ 
+ 		memcpy(&offset, ptr, sizeof(offset));
+ 		ptr += sizeof(offset);
+ 		memcpy(&length, ptr, sizeof(length));
+ 		ptr += sizeof(length);
+ 		ptr += length;
+ 
+ 		if (ptr < end)
+ 			appendStringInfo(buf, "offset %u, length %u; ", offset, length);
+ 		else
+ 			appendStringInfo(buf, "offset %u, length %u", offset, length);
+ 	}
+ 
+ 	return;
+ }
+ 
+ /*
+  * Identification of generic xlog record: we don't distinguish any subtypes
+  * inside generic xlog records.
+  */
+ const char *
+ generic_identify(uint8 info)
+ {
+ 	return "Generic";
+ }
diff --git a/src/backend/access/transam/Makefile b/src/backend/access/transam/Makefile
new file mode 100644
index 94455b2..16fbe47
*** a/src/backend/access/transam/Makefile
--- b/src/backend/access/transam/Makefile
*************** subdir = src/backend/access/transam
*** 12,19 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = clog.o commit_ts.o multixact.o parallel.o rmgr.o slru.o subtrans.o \
! 	timeline.o transam.o twophase.o twophase_rmgr.o varsup.o \
  	xact.o xlog.o xlogarchive.o xlogfuncs.o \
  	xloginsert.o xlogreader.o xlogutils.o
  
--- 12,19 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = clog.o commit_ts.o generic_xlog.o multixact.o parallel.o rmgr.o slru.o \
! 	subtrans.o timeline.o transam.o twophase.o twophase_rmgr.o varsup.o \
  	xact.o xlog.o xlogarchive.o xlogfuncs.o \
  	xloginsert.o xlogreader.o xlogutils.o
  
diff --git a/src/backend/access/transam/generic_xlog.c b/src/backend/access/transam/generic_xlog.c
new file mode 100644
index ...e6f26a5
*** a/src/backend/access/transam/generic_xlog.c
--- b/src/backend/access/transam/generic_xlog.c
***************
*** 0 ****
--- 1,508 ----
+ /*-------------------------------------------------------------------------
+  *
+  * generic_xlog.c
+  *	 Implementation of generic xlog records.
+  *
+  *
+  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/backend/access/transam/generic_xlog.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/generic_xlog.h"
+ #include "access/xlogutils.h"
+ #include "miscadmin.h"
+ #include "utils/memutils.h"
+ 
+ /*-------------------------------------------------------------------------
+  * API for construction of generic xlog records
+  *
+  * This API allows user to construct generic xlog records which are
+  * describing difference between pages in general way.  Thus it's useful
+  * for extension which provides custom access methods because they couldn't
+  * register their own WAL redo routines.
+  *
+  * Generic xlog record should be constructed in following steps.
+  * 1) GenericXLogStart(relation) - start construction of generic xlog
+  *	  record for given relation.
+  * 2) GenericXLogRegister(buffer, isNew) - register one or more buffers
+  *	  for generic xlog record.  This function return a copy of page image
+  *	  where modifications should be performed.  The second argument
+  *	  indicates that block is new and full image should be taken.
+  * 3) Do modification of page images obtained in previous step.
+  * 4) GenericXLogFinish() - finish construction of generic xlog record.
+  *
+  * Please, note following points while constructing generic xlog records.
+  * - No direct modifications of page images are allowed! All modifications
+  *	 should be done in copies returned by GenericXLogRegister().  Literally
+  *	 code which makes generic xlog records should never call
+  *	 BufferGetPage() function.
+  * - On any step generic xlog record construction could be canceled by
+  *	 calling GenericXLogAbort().  All changes made in page images copies
+  *	 would be discarded.
+  * - Registrations of buffers (step 2) and modifications of page images
+  *	 (step 3) could be mixed in any sequence.  The only restriction is that
+  *	 you can modify page image only after registration of corresponding
+  *	 buffer.
+  * - After registration buffer also can be unregistered by calling
+  *	 GenericXLogUnregister(buffer).  In this case changes made in particular
+  *	 page image copy will be discarded.
+  * - Generic xlog assumes that pages are using standard layout.  I.e. all
+  *	 information between pd_lower and pd_upper will be discarded.
+  * - Maximum number of buffers simultaneously registered for generic xlog
+  *	 is MAX_GENERIC_XLOG_PAGES.  Error would be thrown if this limit
+  *	 exceeded.
+  * - Since you modify copies of page images, GenericXLogStart() doesn't
+  *	 start a critical section.  Thus, you can do memory allocation, error
+  *	 throwing etc between GenericXLogStart() and GenericXLogFinish().
+  *	 Actual critical section present inside GenericXLogFinish().
+  * - GenericXLogFinish() takes care about marking buffers dirty and setting
+  *	 their LSNs.  You don't need to do this explicitly.
+  * - For unlogged relations, everything work the same expect there is no
+  *	 WAL record produced.  Thus, you typically don't need to do any explicit
+  *	 checks for unlogged relations.
+  * - If registered buffer isn't new, generic xlog record contains delta
+  *	 between old and new page images.  This delta is produced by per byte
+  *	 comparison.  Current delta mechanist is not effective for data shift
+  *	 inside the page.  However, it could be improved in further versions.
+  * - Generic xlog redo function will acquire exclusive locks to buffers
+  *	 in the same order they were registered.  After redo of all changes
+  *	 locks would be released in the same order.  That could makes sense for
+  *	 concurrency.
+  *
+  * Internally delta between pages consists of set of fragments.  Each fragment
+  * represents changes made in given region of page.  Fragment is described
+  * as following.
+  *
+  * - offset of page region (OffsetNumber)
+  * - length of page region (OffsetNumber)
+  * - data - the data to place into described region ('length' number of bytes)
+  *
+  * Unchanged regions of page are uncovered by these fragments.  This is why
+  * delta could be more compact than full page image.  But if unchanged region
+  * of page is less than fragment header (offset and length) then it would
+  * increase size of delta instead of decreasing.  Thus, we break fragment only
+  * for unchanged regions greater than MATCH_THRESHOLD.
+  *
+  * The worst case for delta size is when we didn't find any unchanged region
+  * in the page. Then size of delta would be size of page plus size of fragment
+  * header.
+  */
+ #define FRAGMENT_HEADER_SIZE	(2 * sizeof(OffsetNumber))
+ #define MATCH_THRESHOLD			FRAGMENT_HEADER_SIZE
+ #define MAX_DELTA_SIZE			BLCKSZ + FRAGMENT_HEADER_SIZE
+ 
+ /* Struct of generic xlog data for single page */
+ typedef struct
+ {
+ 	Buffer	buffer;			/* registered buffer */
+ 	char	image[BLCKSZ];	/* copy of page image for modification */
+ 	char	data[MAX_DELTA_SIZE]; /* delta between page images */
+ 	int		dataLen;		/* space consumed in data field */
+ 	bool	fullImage;		/* are we taking full image of this page? */
+ } PageData;
+ 
+ /* Enum of generic xlog (gxlog) status */
+ enum GenericXlogStatus
+ {
+ 	GXLOG_NOT_STARTED,	/* gxlog is not started */
+ 	GXLOG_LOGGED,		/* gxlog is started for logged relation */
+ 	GXLOG_UNLOGGED		/* gxlog is started for unlogged relation */
+ };
+ 
+ static enum GenericXlogStatus	genericXlogStatus = GXLOG_NOT_STARTED;
+ static PageData					pages[MAX_GENERIC_XLOG_PAGES];
+ 
+ 
+ static void writeFragment(PageData *pageData, OffsetNumber offset,
+ 						  OffsetNumber len, Pointer data);
+ static void writeDelta(PageData *pageData);
+ static void applyPageRedo(Page page, Pointer data, Size dataSize);
+ 
+ /*
+  * Write next fragment into delta.
+  */
+ static void
+ writeFragment(PageData *pageData, OffsetNumber offset, OffsetNumber length,
+ 			  Pointer data)
+ {
+ 	Pointer			ptr = pageData->data + pageData->dataLen;
+ 
+ 	/* Check we have enough of space */
+ 	Assert(pageData->dataLen + sizeof(offset) +
+ 		   sizeof(length) + length <= sizeof(pageData->data));
+ 
+ 	/* Write fragment data */
+ 	memcpy(ptr, &offset, sizeof(offset));
+ 	ptr += sizeof(offset);
+ 	memcpy(ptr, &length, sizeof(length));
+ 	ptr += sizeof(length);
+ 	memcpy(ptr, data, length);
+ 	ptr += length;
+ 
+ 	pageData->dataLen = ptr - pageData->data;
+ }
+ 
+ /*
+  * Make delta for given page.
+  */
+ static void
+ writeDelta(PageData *pageData)
+ {
+ 	Page			page = BufferGetPage(pageData->buffer),
+ 					image = (Page) pageData->image;
+ 	int				i,
+ 					fragmentBegin = -1,
+ 					fragmentEnd = -1;
+ 	uint16			pageLower = ((PageHeader) page)->pd_lower,
+ 					pageUpper = ((PageHeader) page)->pd_upper,
+ 					imageLower = ((PageHeader) image)->pd_lower,
+ 					imageUpper = ((PageHeader) image)->pd_upper;
+ 
+ 	for (i = 0; i < BLCKSZ; i++)
+ 	{
+ 		bool	match;
+ 
+ 		/*
+ 		 * Check if bytes in old and new page images matches.  We don't rely
+ 		 * data in unallocated area between pd_lower and pd_upper.  Thus we
+ 		 * assume unallocated area to expand with unmatched bytes.  Bytes
+ 		 * inside unallocated area are assumed to always match.
+ 		 */
+ 		if (i < pageLower)
+ 		{
+ 			if (i < imageLower)
+ 				match = (page[i] == image[i]);
+ 			else
+ 				match = false;
+ 		}
+ 		else if (i >= pageUpper)
+ 		{
+ 			if (i >= imageUpper)
+ 				match = (page[i] == image[i]);
+ 			else
+ 				match = false;
+ 		}
+ 		else
+ 		{
+ 			match = true;
+ 		}
+ 
+ 		if (match)
+ 		{
+ 			if (fragmentBegin >= 0)
+ 			{
+ 				/* Matched byte is potential of fragment. */
+ 				if (fragmentEnd < 0)
+ 					fragmentEnd = i;
+ 
+ 				/*
+ 				 * Write next fragment if sequence of matched bytes is longer
+ 				 * than MATCH_THRESHOLD.
+ 				 */
+ 				if (i - fragmentEnd >= MATCH_THRESHOLD)
+ 				{
+ 					writeFragment(pageData, fragmentBegin,
+ 								  fragmentEnd - fragmentBegin,
+ 								  page + fragmentBegin);
+ 					fragmentBegin = -1;
+ 					fragmentEnd = -1;
+ 				}
+ 			}
+ 		}
+ 		else
+ 		{
+ 			/* On unmatched byte, start new fragment if it's not done yet */
+ 			if (fragmentBegin < 0)
+ 				fragmentBegin = i;
+ 			fragmentEnd = -1;
+ 		}
+ 	}
+ 
+ 	if (fragmentBegin >= 0)
+ 		writeFragment(pageData, fragmentBegin,
+ 					  BLCKSZ - fragmentBegin,
+ 					  page + fragmentBegin);
+ 
+ #ifdef WAL_DEBUG
+ 	/*
+ 	 * If xlog debug is enabled then check produced delta.  Result of delta
+ 	 * application to saved image should be the same as current page state.
+ 	 */
+ 	if (XLOG_DEBUG)
+ 	{
+ 		char	tmp[BLCKSZ];
+ 		memcpy(tmp, image, BLCKSZ);
+ 		applyPageRedo(tmp, pageData->data, pageData->dataLen);
+ 		elog(ERROR, "result of generic xlog apply doesn't match");
+ 	}
+ #endif
+ }
+ 
+ /*
+  * Start new generic xlog record.
+  */
+ void
+ GenericXLogStart(Relation relation)
+ {
+ 	int i;
+ 
+ 	if (genericXlogStatus != GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogStart: generic xlog is already started")));
+ 
+ 	genericXlogStatus = RelationNeedsWAL(relation) ? GXLOG_LOGGED : GXLOG_UNLOGGED;
+ 
+ 	for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 	{
+ 		pages[i].buffer = InvalidBuffer;
+ 	}
+ }
+ 
+ /*
+  * Register new buffer for generic xlog record.
+  */
+ Page
+ GenericXLogRegister(Buffer buffer, bool isNew)
+ {
+ 	int block_id;
+ 
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogRegister: generic xlog isn't started")));
+ 
+ 	/* Place new buffer to unused slot in array */
+ 	for (block_id = 0; block_id < MAX_GENERIC_XLOG_PAGES; block_id++)
+ 	{
+ 		if (BufferIsInvalid(pages[block_id].buffer))
+ 		{
+ 			pages[block_id].buffer = buffer;
+ 			memcpy(pages[block_id].image, BufferGetPage(buffer), BLCKSZ);
+ 			pages[block_id].dataLen = 0;
+ 			pages[block_id].fullImage = isNew;
+ 			return (Page)pages[block_id].image;
+ 		}
+ 		else if (pages[block_id].buffer == buffer)
+ 		{
+ 			/* 
+ 			 * Buffer already registered.  Just return image which is already
+ 			 * prepared.
+ 			 */
+ 			return (Page)pages[block_id].image;
+ 		}
+ 	}
+ 
+ 	ereport(ERROR,
+ 			(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ 			 errmsg("GenericXLogRegister: maximum number of %d buffers is exceeded",
+ 					MAX_GENERIC_XLOG_PAGES)));
+ 
+ 	/* keep compiler quiet */
+ 	return NULL;
+ }
+ 
+ /*
+  * Unregister particular buffer for generic xlog record.
+  */
+ void
+ GenericXLogUnregister(Buffer buffer)
+ {
+ 	int block_id;
+ 
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogUnregister: generic xlog isn't started")));
+ 
+ 	/* Find block in array to unregister */
+ 	for (block_id = 0; block_id < MAX_GENERIC_XLOG_PAGES; block_id++)
+ 	{
+ 		if (pages[block_id].buffer == buffer)
+ 		{
+ 			/*
+ 			 * Preserve order of pages in array because it could matter for
+ 			 * concurrency.
+ 			 */
+ 			memmove(&pages[block_id], &pages[block_id + 1],
+ 					(MAX_GENERIC_XLOG_PAGES - block_id - 1) * sizeof(PageData));
+ 			pages[MAX_GENERIC_XLOG_PAGES - 1].buffer = InvalidBuffer;
+ 			return;
+ 		}
+ 	}
+ 
+ 	ereport(ERROR,
+ 			(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 			 errmsg("GenericXLogUnregister: registered buffer not found")));
+ }
+ 
+ /*
+  * Put all changes in registered buffers to generic xlog record.
+  */
+ XLogRecPtr
+ GenericXLogFinish(void)
+ {
+ 	XLogRecPtr lsn = InvalidXLogRecPtr;
+ 	int i;
+ 
+ 	if (genericXlogStatus == GXLOG_LOGGED)
+ 	{
+ 		/* Logged relation: make xlog record in critical section. */
+ 		START_CRIT_SECTION();
+ 		XLogBeginInsert();
+ 
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			char	tmp[BLCKSZ];
+ 
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 
+ 			/* Swap current and saved page image. */
+ 			memcpy(tmp, pages[i].image, BLCKSZ);
+ 			memcpy(pages[i].image, BufferGetPage(pages[i].buffer), BLCKSZ);
+ 			memcpy(BufferGetPage(pages[i].buffer), tmp, BLCKSZ);
+ 
+ 			if (pages[i].fullImage)
+ 			{
+ 				/* Full page image doesn't require anything special from us */
+ 				XLogRegisterBuffer(i, pages[i].buffer, REGBUF_FORCE_IMAGE);
+ 			}
+ 			else
+ 			{
+ 				/*
+ 				 * In normal node calculate delta and write use it as data
+ 				 * associated with this page.
+ 				 */
+ 				XLogRegisterBuffer(i, pages[i].buffer, REGBUF_STANDARD);
+ 				writeDelta(&pages[i]);
+ 				XLogRegisterBufData(i, pages[i].data, pages[i].dataLen);
+ 			}
+ 		}
+ 
+ 		/* Insert xlog record */
+ 		lsn = XLogInsert(RM_GENERIC_ID, 0);
+ 
+ 		/* Set LSN and make buffers dirty */
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 			PageSetLSN(BufferGetPage(pages[i].buffer), lsn);
+ 			MarkBufferDirty(pages[i].buffer);
+ 		}
+ 		END_CRIT_SECTION();
+ 	}
+ 	else if (genericXlogStatus == GXLOG_UNLOGGED)
+ 	{
+ 		/* Unlogged relation: skip xlog-related stuff */
+ 		START_CRIT_SECTION();
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 			memcpy(BufferGetPage(pages[i].buffer), pages[i].image, BLCKSZ);
+ 			MarkBufferDirty(pages[i].buffer);
+ 		}
+ 		END_CRIT_SECTION();
+ 	}
+ 	else
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogFinish: generic xlog isn't started")));
+ 	}
+ 
+ 	genericXlogStatus = GXLOG_NOT_STARTED;
+ 
+ 	return lsn;
+ }
+ 
+ /*
+  * Abort generic xlog record.
+  */
+ void
+ GenericXLogAbort(void)
+ {
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogAbort: generic xlog isn't started")));
+ 
+ 	genericXlogStatus = GXLOG_NOT_STARTED;
+ }
+ 
+ /*
+  * Apply delta to given page image.
+  */
+ static void
+ applyPageRedo(Page page, Pointer data, Size dataSize)
+ {
+ 	Pointer ptr = data, end = data + dataSize;
+ 
+ 	while (ptr < end)
+ 	{
+ 		OffsetNumber	offset,
+ 						length;
+ 
+ 		memcpy(&offset, ptr, sizeof(offset));
+ 		ptr += sizeof(offset);
+ 		memcpy(&length, ptr, sizeof(length));
+ 		ptr += sizeof(length);
+ 
+ 		memcpy(page + offset, ptr, length);
+ 
+ 		ptr += length;
+ 	}
+ }
+ 
+ /*
+  * Redo function for generic xlog record.
+  */
+ void
+ generic_redo(XLogReaderState *record)
+ {
+ 	uint8		block_id;
+ 	Buffer		buffers[MAX_GENERIC_XLOG_PAGES] = {InvalidBuffer};
+ 	XLogRecPtr	lsn = record->EndRecPtr;
+ 
+ 	Assert(record->max_block_id < MAX_GENERIC_XLOG_PAGES);
+ 
+ 	/* Interate over blocks */
+ 	for (block_id = 0; block_id <= record->max_block_id; block_id++)
+ 	{
+ 		XLogRedoAction action;
+ 
+ 		if (!XLogRecHasBlockRef(record, block_id))
+ 			continue;
+ 
+ 		action = XLogReadBufferForRedo(record, block_id, &buffers[block_id]);
+ 
+ 		/* Apply redo to given block if needed */
+ 		if (action == BLK_NEEDS_REDO)
+ 		{
+ 			Pointer	blockData;
+ 			Size	blockDataSize;
+ 			Page	page;
+ 
+ 			page = BufferGetPage(buffers[block_id]);
+ 			blockData = XLogRecGetBlockData(record, block_id, &blockDataSize);
+ 			applyPageRedo(page, blockData, blockDataSize);
+ 
+ 			PageSetLSN(page, lsn);
+ 			MarkBufferDirty(buffers[block_id]);
+ 		}
+ 	}
+ 
+ 	/* Changes are done: unlock and release all buffers */
+ 	for (block_id = 0; block_id <= record->max_block_id; block_id++)
+ 	{
+ 		if (BufferIsValid(buffers[block_id]))
+ 			UnlockReleaseBuffer(buffers[block_id]);
+ 	}
+ }
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
new file mode 100644
index 7c4d773..7b38c16
*** a/src/backend/access/transam/rmgr.c
--- b/src/backend/access/transam/rmgr.c
***************
*** 11,16 ****
--- 11,17 ----
  #include "access/commit_ts.h"
  #include "access/gin.h"
  #include "access/gist_private.h"
+ #include "access/generic_xlog.h"
  #include "access/hash.h"
  #include "access/heapam_xlog.h"
  #include "access/brin_xlog.h"
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
new file mode 100644
index 13af485..262deb2
*** a/src/backend/replication/logical/decode.c
--- b/src/backend/replication/logical/decode.c
*************** LogicalDecodingProcessRecord(LogicalDeco
*** 143,148 ****
--- 143,149 ----
  		case RM_BRIN_ID:
  		case RM_COMMIT_TS_ID:
  		case RM_REPLORIGIN_ID:
+ 		case RM_GENERIC_ID:
  			/* just deal with xid, and done */
  			ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(record),
  									buf.origptr);
diff --git a/src/bin/pg_xlogdump/.gitignore b/src/bin/pg_xlogdump/.gitignore
new file mode 100644
index eebaf30..33a1acf
*** a/src/bin/pg_xlogdump/.gitignore
--- b/src/bin/pg_xlogdump/.gitignore
***************
*** 4,9 ****
--- 4,10 ----
  /clogdesc.c
  /committsdesc.c
  /dbasedesc.c
+ /genericdesc.c
  /gindesc.c
  /gistdesc.c
  /hashdesc.c
diff --git a/src/bin/pg_xlogdump/rmgrdesc.c b/src/bin/pg_xlogdump/rmgrdesc.c
new file mode 100644
index f9cd395..cff7e59
*** a/src/bin/pg_xlogdump/rmgrdesc.c
--- b/src/bin/pg_xlogdump/rmgrdesc.c
***************
*** 11,16 ****
--- 11,17 ----
  #include "access/brin_xlog.h"
  #include "access/clog.h"
  #include "access/commit_ts.h"
+ #include "access/generic_xlog.h"
  #include "access/gin.h"
  #include "access/gist_private.h"
  #include "access/hash.h"
diff --git a/src/include/access/generic_xlog.h b/src/include/access/generic_xlog.h
new file mode 100644
index ...49249e0
*** a/src/include/access/generic_xlog.h
--- b/src/include/access/generic_xlog.h
***************
*** 0 ****
--- 1,36 ----
+ /*-------------------------------------------------------------------------
+  *
+  * generic_xlog.h
+  *	  Generic xlog API definition.
+  *
+  *
+  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/access/generic_xlog.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef GENERIC_XLOG_H
+ #define GENERIC_XLOG_H
+ 
+ #include "access/xlog.h"
+ #include "access/xlog_internal.h"
+ #include "storage/bufpage.h"
+ #include "utils/rel.h"
+ 
+ #define MAX_GENERIC_XLOG_PAGES	  3
+ 
+ /* API for construction of generic xlog records */
+ extern void GenericXLogStart(Relation relation);
+ extern Page GenericXLogRegister(Buffer buffer, bool isNew);
+ extern void GenericXLogUnregister(Buffer buffer);
+ extern XLogRecPtr GenericXLogFinish(void);
+ extern void GenericXLogAbort(void);
+ 
+ /* functions defined for rmgr */
+ extern void generic_redo(XLogReaderState *record);
+ extern const char *generic_identify(uint8 info);
+ extern void generic_desc(StringInfo buf, XLogReaderState *record);
+ 
+ #endif   /* GENERIC_XLOG_H */
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
new file mode 100644
index fab912d..3cfe6f7
*** a/src/include/access/rmgrlist.h
--- b/src/include/access/rmgrlist.h
*************** PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo
*** 45,47 ****
--- 45,48 ----
  PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL)
  PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL)
  PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL)
+ PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL)
0003-bloom-contrib.11.patchapplication/octet-stream; name=0003-bloom-contrib.11.patchDownload
diff --git a/contrib/Makefile b/contrib/Makefile
new file mode 100644
index d12dd63..25263c0
*** a/contrib/Makefile
--- b/contrib/Makefile
*************** SUBDIRS = \
*** 8,13 ****
--- 8,14 ----
  		adminpack	\
  		auth_delay	\
  		auto_explain	\
+ 		bloom		\
  		btree_gin	\
  		btree_gist	\
  		chkpass		\
diff --git a/contrib/bloom/.gitignore b/contrib/bloom/.gitignore
new file mode 100644
index ...5dcb3ff
*** a/contrib/bloom/.gitignore
--- b/contrib/bloom/.gitignore
***************
*** 0 ****
--- 1,4 ----
+ # Generated subdirectories
+ /log/
+ /results/
+ /tmp_check/
diff --git a/contrib/bloom/Makefile b/contrib/bloom/Makefile
new file mode 100644
index ...13bd397
*** a/contrib/bloom/Makefile
--- b/contrib/bloom/Makefile
***************
*** 0 ****
--- 1,24 ----
+ # contrib/bloom/Makefile
+ 
+ MODULE_big = bloom
+ OBJS = blcost.o blinsert.o blscan.o blutils.o blvacuum.o blvalidate.o $(WIN32RES)
+ 
+ EXTENSION = bloom
+ DATA = bloom--1.0.sql
+ PGFILEDESC = "bloom access method - signature file based index"
+ 
+ REGRESS = bloom
+ 
+ ifdef USE_PGXS
+ PG_CONFIG = pg_config
+ PGXS := $(shell $(PG_CONFIG) --pgxs)
+ include $(PGXS)
+ else
+ subdir = contrib/bloom
+ top_builddir = ../..
+ include $(top_builddir)/src/Makefile.global
+ include $(top_srcdir)/contrib/contrib-global.mk
+ endif
+ 
+ wal-check: temp-install
+ 	$(prove_check)
diff --git a/contrib/bloom/blcost.c b/contrib/bloom/blcost.c
new file mode 100644
index ...d918dce
*** a/contrib/bloom/blcost.c
--- b/contrib/bloom/blcost.c
***************
*** 0 ****
--- 1,45 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blcost.c
+  *		Cost estimate function for bloom indexes.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blcost.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "fmgr.h"
+ #include "optimizer/cost.h"
+ #include "utils/selfuncs.h"
+ 
+ #include "bloom.h"
+ 
+ void
+ blcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
+ 			   Cost *indexStartupCost, Cost *indexTotalCost,
+ 			   Selectivity *indexSelectivity, double *indexCorrelation)
+ {
+ 	IndexOptInfo   *index = path->indexinfo;
+ 	List		   *qinfos;
+ 	GenericCosts	costs;
+ 
+ 	/* Do preliminary analysis of indexquals */
+ 	qinfos = deconstruct_indexquals(path);
+ 
+ 	MemSet(&costs, 0, sizeof(costs));
+ 
+ 	/* We have to visit all index tuples anyway */
+ 	costs.numIndexTuples = index->tuples;
+ 
+ 	/* Use generic estimate */
+ 	genericcostestimate(root, path, loop_count, qinfos, &costs);
+ 
+ 	*indexStartupCost = costs.indexStartupCost;
+ 	*indexTotalCost = costs.indexTotalCost;
+ 	*indexSelectivity = costs.indexSelectivity;
+ 	*indexCorrelation = costs.indexCorrelation;
+ }
diff --git a/contrib/bloom/blinsert.c b/contrib/bloom/blinsert.c
new file mode 100644
index ...15bff40
*** a/contrib/bloom/blinsert.c
--- b/contrib/bloom/blinsert.c
***************
*** 0 ****
--- 1,293 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blinsert.c
+  *		Bloom index build and insert functions.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blinsert.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "access/generic_xlog.h"
+ #include "catalog/index.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ 
+ #include "bloom.h"
+ 
+ PG_MODULE_MAGIC;
+ 
+ typedef struct
+ {
+ 	BloomState		blstate;
+ 	MemoryContext	tmpCtx;
+ 	char			data[BLCKSZ];
+ 	int64			count;
+ } BloomBuildState;
+ 
+ /*
+  * Flush page cached in BloomBuildState.
+  */
+ static void
+ flushCachedPage(Relation index, BloomBuildState *buildstate)
+ {
+ 	Page	page;
+ 	Buffer	buffer = BloomNewBuffer(index);
+ 
+ 	GenericXLogStart(index);
+ 	page = GenericXLogRegister(buffer, true);
+ 	memcpy(page, buildstate->data, BLCKSZ);
+ 	GenericXLogFinish();
+ 	UnlockReleaseBuffer(buffer);
+ }
+ 
+ /*
+  * (Re)initialize cached page in BloomBuildState.
+  */
+ static void
+ initCachedPage(BloomBuildState *buildstate)
+ {
+ 	memset(buildstate->data, 0, BLCKSZ);
+ 	BloomInitPage(buildstate->data, 0);
+ 	buildstate->count = 0;
+ }
+ 
+ /*
+  * Per-tuple callback from IndexBuildHeapScan.
+  */
+ static void
+ bloomBuildCallback(Relation index, HeapTuple htup, Datum *values,
+ 					bool *isnull, bool tupleIsAlive, void *state)
+ {
+ 	BloomBuildState	*buildstate = (BloomBuildState *)state;
+ 	MemoryContext	 oldCtx;
+ 	BloomTuple		*itup;
+ 
+ 	oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
+ 
+ 	itup = BloomFormTuple(&buildstate->blstate, &htup->t_self, values, isnull);
+ 
+ 	/* Try to add next item to cached page */
+ 	if (BloomPageAddItem(&buildstate->blstate, buildstate->data, itup) == false)
+ 	{
+ 		/* Cached page is full, flush it out and make a new one */
+ 		flushCachedPage(index, buildstate);
+ 
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		initCachedPage(buildstate);
+ 
+ 		if (BloomPageAddItem(&buildstate->blstate, buildstate->data, itup) == false)
+ 		{
+ 			/* We shouldn't be here since we're inserting to the empty page */
+ 			elog(ERROR, "can not add new tuple");
+ 		}
+ 	}
+ 	else
+ 	{
+ 		buildstate->count++;
+ 	}
+ 
+ 	MemoryContextSwitchTo(oldCtx);
+ 	MemoryContextReset(buildstate->tmpCtx);
+ }
+ 
+ IndexBuildResult *
+ blbuild(Relation heap, Relation index, IndexInfo *indexInfo)
+ {
+ 	IndexBuildResult   *result;
+ 	double				reltuples;
+ 	BloomBuildState		buildstate;
+ 
+ 	if (RelationGetNumberOfBlocks(index) != 0)
+ 		elog(ERROR, "index \"%s\" already contains data",
+ 			RelationGetRelationName(index));
+ 
+ 	/* Initialize the meta page */
+ 	BloomInitMetapage(index);
+ 
+ 	initBloomState(&buildstate.blstate, index);
+ 
+ 	buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
+ 												"Bloom build temporary context",
+ 												ALLOCSET_DEFAULT_MINSIZE,
+ 												ALLOCSET_DEFAULT_INITSIZE,
+ 												ALLOCSET_DEFAULT_MAXSIZE);
+ 
+ 	initCachedPage(&buildstate);
+ 
+ 	/* Do the heap scan */
+ 	reltuples = IndexBuildHeapScan(heap, index, indexInfo, true,
+ 									bloomBuildCallback, (void *) &buildstate);
+ 
+ 	if (buildstate.count > 0)
+ 		flushCachedPage(index, &buildstate);
+ 
+ 	MemoryContextDelete(buildstate.tmpCtx);
+ 
+ 	result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
+ 	result->heap_tuples = result->index_tuples = reltuples;
+ 
+ 	return result;
+ }
+ 
+ void
+ blbuildempty(Relation index)
+ {
+ 	if (RelationGetNumberOfBlocks(index) != 0)
+ 		elog(ERROR, "index \"%s\" already contains data",
+ 			RelationGetRelationName(index));
+ 
+ 	/* Initialize the meta page */
+ 	BloomInitMetapage(index);
+ }
+ 
+ bool
+ blinsert(Relation index, Datum *values, bool *isnull,
+ 		ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique)
+ {
+ 	BloomState			blstate;
+ 	BloomTuple		   *itup;
+ 	MemoryContext		oldCtx;
+ 	MemoryContext		insertCtx;
+ 	BloomMetaPageData  *metaData;
+ 	Buffer				buffer,
+ 						metaBuffer;
+ 	Page				page,
+ 						metaPage;
+ 	BlockNumber			blkno = InvalidBlockNumber;
+ 	OffsetNumber		nStart;
+ 
+ 	insertCtx = AllocSetContextCreate(CurrentMemoryContext,
+ 										"Bloom insert temporary context",
+ 										ALLOCSET_DEFAULT_MINSIZE,
+ 										ALLOCSET_DEFAULT_INITSIZE,
+ 										ALLOCSET_DEFAULT_MAXSIZE);
+ 
+ 	oldCtx = MemoryContextSwitchTo(insertCtx);
+ 
+ 	initBloomState(&blstate, index);
+ 	itup = BloomFormTuple(&blstate, ht_ctid, values, isnull);
+ 
+ 	/*
+ 	 * At first, try to insert new tuple to the first page in
+ 	 * notFullPage array.  If success we don't need to modify the
+ 	 * meta page.
+ 	 */
+ 	metaBuffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 	LockBuffer(metaBuffer, BUFFER_LOCK_SHARE);
+ 	metaData = BloomPageGetMeta(BufferGetPage(metaBuffer));
+ 
+ 	if (metaData->nEnd > metaData->nStart)
+ 	{
+ 		Page	page;
+ 
+ 		blkno = metaData->notFullPage[metaData->nStart];
+ 
+ 		Assert(blkno != InvalidBlockNumber);
+ 		LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		GenericXLogStart(index);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		if (BloomPageAddItem(&blstate, page, itup))
+ 		{
+ 			GenericXLogFinish();
+ 			UnlockReleaseBuffer(buffer);
+ 			ReleaseBuffer(metaBuffer);
+ 			goto away;
+ 		}
+ 		else
+ 		{
+ 			GenericXLogAbort();
+ 			UnlockReleaseBuffer(buffer);
+ 		}
+ 	}
+ 	else
+ 	{
+ 		/* First page in notFullPage isn't suitable */
+ 		LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
+ 	}
+ 
+ 	/*
+ 	 * Try other pages in notFullPage array.  We will have to change nStart
+ 	 * in metapage.  Thus, grab exclusive lock on metapage.
+ 	 */
+ 	LockBuffer(metaBuffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 	GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(metaBuffer, false);
+ 	metaData = BloomPageGetMeta(metaPage);
+ 
+ 	/*
+ 	 * Iterate over notFullPage array.  Skip page we already tried first.
+ 	 */
+ 	nStart = metaData->nStart;
+ 	if (metaData->nEnd > nStart &&
+ 		blkno == metaData->notFullPage[nStart] )
+ 		nStart++;
+ 
+ 	while (metaData->nEnd > nStart)
+ 	{
+ 		blkno = metaData->notFullPage[nStart];
+ 		Assert(blkno != InvalidBlockNumber);
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		if (BloomPageAddItem(&blstate, page, itup))
+ 		{
+ 			metaData->nStart = nStart;
+ 			GenericXLogFinish();
+ 			UnlockReleaseBuffer(buffer);
+ 			UnlockReleaseBuffer(metaBuffer);
+ 			goto away;
+ 		}
+ 		else
+ 		{
+ 			GenericXLogUnregister(buffer);
+ 			UnlockReleaseBuffer(buffer);
+ 		}
+ 		nStart++;
+ 	}
+ 
+ 	GenericXLogAbort();
+ 
+ 	/*
+ 	 * Didn't find place to insert in notFullPage array.  Allocate new page.
+ 	 */
+ 	buffer = BloomNewBuffer(index);
+ 
+ 	GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(metaBuffer, false);
+ 	metaData = BloomPageGetMeta(metaPage);
+ 	page = GenericXLogRegister(buffer, true);
+ 	BloomInitPage(page, 0);
+ 	BloomPageAddItem(&blstate, page, itup);
+ 
+ 	metaData->nStart = 0;
+ 	metaData->nEnd = 1;
+ 	metaData->notFullPage[ 0 ] = BufferGetBlockNumber(buffer);
+ 
+ 	GenericXLogFinish();
+ 
+ 	UnlockReleaseBuffer(buffer);
+ 	UnlockReleaseBuffer(metaBuffer);
+ 
+ away:
+ 	MemoryContextSwitchTo(oldCtx);
+ 	MemoryContextDelete(insertCtx);
+ 
+ 	return false;
+ }
diff --git a/contrib/bloom/bloom--1.0.sql b/contrib/bloom/bloom--1.0.sql
new file mode 100644
index ...7fa7513
*** a/contrib/bloom/bloom--1.0.sql
--- b/contrib/bloom/bloom--1.0.sql
***************
*** 0 ****
--- 1,19 ----
+ CREATE OR REPLACE FUNCTION blhandler(internal)
+ RETURNS index_am_handler
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C;
+ 
+ -- Access method
+ CREATE ACCESS METHOD bloom TYPE INDEX HANDLER blhandler;
+ 
+ -- Opclasses
+ 
+ CREATE OPERATOR CLASS int4_ops
+ DEFAULT FOR TYPE int4 USING bloom AS
+ 	OPERATOR	1	=(int4, int4),
+ 	FUNCTION	1	hashint4(int4);
+ 
+ CREATE OPERATOR CLASS text_ops
+ DEFAULT FOR TYPE text USING bloom AS
+ 	OPERATOR	1	=(text, text),
+ 	FUNCTION	1	hashtext(text);
diff --git a/contrib/bloom/bloom.control b/contrib/bloom/bloom.control
new file mode 100644
index ...4d4124b
*** a/contrib/bloom/bloom.control
--- b/contrib/bloom/bloom.control
***************
*** 0 ****
--- 1,5 ----
+ # bloom extension
+ comment = 'bloom access method - signature file based index'
+ default_version = '1.0'
+ module_pathname = '$libdir/bloom'
+ relocatable = true
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
new file mode 100644
index ...6f20ee4
*** a/contrib/bloom/bloom.h
--- b/contrib/bloom/bloom.h
***************
*** 0 ****
--- 1,176 ----
+ /*-------------------------------------------------------------------------
+  *
+  * bloom.h
+  *	  Header for bloom index.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/bloom.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef _BLOOM_H_
+ #define _BLOOM_H_
+ 
+ #include "access/amapi.h"
+ #include "access/generic_xlog.h"
+ #include "access/itup.h"
+ #include "access/xlog.h"
+ #include "nodes/relation.h"
+ #include "fmgr.h"
+ 
+ /* Support procedures numbers */
+ #define	BLOOM_HASH_PROC			1
+ #define	BLOOM_NPROC				1
+ 
+ /* Scan strategies */
+ #define	BLOOM_EQUAL_STRATEGY	1
+ #define	BLOOM_NSTRATEGIES		1
+ 
+ /* Opaque for bloom pages */
+ typedef struct BloomPageOpaqueData
+ {
+ 	OffsetNumber	maxoff;
+ 	uint16			flags;
+ } BloomPageOpaqueData;
+ 
+ typedef BloomPageOpaqueData *BloomPageOpaque;
+ 
+ /* Bloom page flags */
+ #define BLOOM_META		(1<<0)
+ #define BLOOM_DELETED	(2<<0)
+ 
+ #define BloomPageGetOpaque(page) ((BloomPageOpaque) PageGetSpecialPointer(page))
+ #define BloomPageGetMaxOffset(page) (BloomPageGetOpaque(page)->maxoff)
+ #define BloomPageIsMeta(page) (BloomPageGetOpaque(page)->flags & BLOOM_META)
+ #define BloomPageIsDeleted(page) (BloomPageGetOpaque(page)->flags & BLOOM_DELETED)
+ #define BloomPageSetDeleted(page) (BloomPageGetOpaque(page)->flags |= BLOOM_DELETED)
+ #define BloomPageSetNonDeleted(page) (BloomPageGetOpaque(page)->flags &= ~BLOOM_DELETED)
+ #define BloomPageGetData(page)		((BloomTuple *)PageGetContents(page))
+ #define BloomPageGetTuple(state, page, offset) \
+ 	((BloomTuple *)(PageGetContents(page) \
+ 		+ (state)->sizeOfBloomTuple * ((offset) - 1)))
+ #define BloomPageGetNextTuple(state, tuple) \
+ 	((BloomTuple *)((Pointer)(tuple) + (state)->sizeOfBloomTuple))
+ 
+ /* Preserved page numbers */
+ #define BLOOM_METAPAGE_BLKNO	(0)
+ #define BLOOM_HEAD_BLKNO		(1) /* first data page */
+ 
+ /* Bloom index options */
+ typedef struct BloomOptions
+ {
+ 	int32	vl_len_;				 /* varlena header (do not touch directly!) */
+ 	int		bloomLength;			 /* length of signature in uint16 */
+ 	int		bitSize[INDEX_MAX_KEYS]; /* signature bits per index key */
+ } BloomOptions;
+ 
+ /*
+  * FreeBlockNumberArray - array of block numbers sized so that metadata fill
+  * all space in metapage.
+  */
+ typedef BlockNumber FreeBlockNumberArray[
+ 	MAXALIGN_DOWN(
+ 		BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(BloomPageOpaqueData))
+ 			   - MAXALIGN(sizeof(uint16) * 2 + sizeof(uint32) + sizeof(BloomOptions))
+ 	) / sizeof(BlockNumber) 
+ ];
+ 
+ /* Metadata of bloom index */
+ typedef struct BloomMetaPageData
+ {
+ 	uint32					magickNumber;
+ 	uint16					nStart;
+ 	uint16					nEnd;
+ 	BloomOptions			opts;
+ 	FreeBlockNumberArray	notFullPage;
+ } BloomMetaPageData;
+ 
+ /* Magic number to distinguish bloom pages among anothers */
+ #define BLOOM_MAGICK_NUMBER	(0xDBAC0DED)
+ 
+ /* Number of blocks numbers fit in BloomMetaPageData */
+ #define BloomMetaBlockN		(sizeof(FreeBlockNumberArray) / sizeof(BlockNumber))
+ 
+ #define BloomPageGetMeta(page)	((BloomMetaPageData *) PageGetContents(page))
+ 
+ typedef struct BloomState 
+ {
+ 	FmgrInfo			hashFn[INDEX_MAX_KEYS];
+ 	BloomOptions	   *opts; /* stored in rd_amcache and defined at creation time */
+ 	int32				nColumns;
+ 	/* 
+ 	 * sizeOfBloomTuple is index's specific, and it depends on
+ 	 * reloptions, so precompute it
+ 	 */
+ 	int32				sizeOfBloomTuple; 
+ } BloomState;
+ 
+ #define BloomPageGetFreeSpace(state, page) \
+ 	(BLCKSZ - MAXALIGN(SizeOfPageHeaderData) \
+ 		- BloomPageGetMaxOffset(page) * (state)->sizeOfBloomTuple \
+ 		- MAXALIGN(sizeof(BloomPageOpaqueData)))
+ 
+ /*
+  * Tuples are very different from all other relations
+  */
+ typedef uint16	SignType;
+ 
+ typedef struct BloomTuple
+ {
+ 	ItemPointerData		heapPtr;
+ 	SignType			sign[1];
+ } BloomTuple;
+ 
+ #define BLOOMTUPLEHDRSZ	offsetof(BloomTuple, sign)
+ 
+ /* Opaque data structure for bloom index scan */
+ typedef struct BloomScanOpaqueData
+ {
+ 	SignType   *sign;	/* Scan signature */
+ 	BloomState	state;
+ } BloomScanOpaqueData;
+ 
+ typedef BloomScanOpaqueData *BloomScanOpaque;
+ 
+ /* blutils.c */
+ extern void _PG_init(void);
+ extern Datum blhandler(PG_FUNCTION_ARGS);
+ extern void initBloomState(BloomState *state, Relation index);
+ extern void BloomInitMetapage(Relation index);
+ extern void BloomInitPage(Page page, uint16 flags);
+ extern Buffer BloomNewBuffer(Relation index);
+ extern void signValue(BloomState *state, SignType *sign, Datum value, int attno);
+ extern BloomTuple* BloomFormTuple(BloomState *state, ItemPointer iptr, Datum *values, bool *isnull);
+ extern bool BloomPageAddItem(BloomState *state, Page page, BloomTuple *tuple);
+ 
+ /* blvalidate.c */
+ extern bool blvalidate(Oid opclassoid);
+ 
+ /* interface functions */
+ extern bool blinsert(Relation index, Datum *values, bool *isnull,
+ 					 ItemPointer ht_ctid, Relation heapRel,
+ 					 IndexUniqueCheck checkUnique);
+ extern IndexScanDesc blbeginscan(Relation r, int nkeys, int norderbys);
+ extern int64 blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
+ extern void blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ 					 ScanKey orderbys, int norderbys);
+ extern void blendscan(IndexScanDesc scan);
+ extern void blmarkpos(IndexScanDesc scan);
+ extern void blrestrpos(IndexScanDesc scan);
+ extern IndexBuildResult *blbuild(Relation heap, Relation index,
+ 								   struct IndexInfo *indexInfo);
+ extern void blbuildempty(Relation index);
+ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
+ 	IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback,
+ 	void *callback_state);
+ extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
+ 	IndexBulkDeleteResult *stats);
+ extern bytea *bloptions(Datum reloptions, bool validate);
+ extern void blcostestimate(PlannerInfo *root, IndexPath *path,
+ 						   double loop_count, Cost *indexStartupCost,
+ 						   Cost *indexTotalCost, Selectivity *indexSelectivity,
+ 						   double *indexCorrelation);
+ 
+ #endif
diff --git a/contrib/bloom/blscan.c b/contrib/bloom/blscan.c
new file mode 100644
index ...467a1b1
*** a/contrib/bloom/blscan.c
--- b/contrib/bloom/blscan.c
***************
*** 0 ****
--- 1,174 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blscan.c
+  *		Bloom index scan functions.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blscan.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/relscan.h"
+ #include "pgstat.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/lmgr.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ 
+ #include "bloom.h"
+ 
+ IndexScanDesc
+ blbeginscan(Relation r, int nkeys, int norderbys)
+ {
+ 	IndexScanDesc scan;
+ 
+ 	scan = RelationGetIndexScan(r, nkeys, norderbys);
+ 
+ 	return scan;
+ }
+ 
+ void
+ blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ 							 ScanKey orderbys, int norderbys)
+ {
+ 	BloomScanOpaque so;
+ 
+ 	so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so == NULL)
+ 	{
+ 		/* if called from blbeginscan */
+ 		so = (BloomScanOpaque) palloc(sizeof(BloomScanOpaqueData));
+ 		initBloomState(&so->state, scan->indexRelation);
+ 		scan->opaque = so;
+ 
+ 	}
+ 	else
+ 	{
+ 		if (so->sign)
+ 			pfree(so->sign);
+ 	}
+ 	so->sign = NULL;
+ 
+ 	if (scankey && scan->numberOfKeys > 0)
+ 	{
+ 		memmove(scan->keyData, scankey,
+ 				scan->numberOfKeys * sizeof(ScanKeyData));
+ 	}
+ }
+ 
+ void
+ blendscan(IndexScanDesc scan)
+ {
+ 	BloomScanOpaque so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so->sign)
+ 		pfree(so->sign);
+ 	so->sign = NULL;
+ }
+ 
+ void
+ blmarkpos(IndexScanDesc scan)
+ {
+ 	elog(ERROR, "Bloom does not support mark/restore");
+ }
+ 
+ void
+ blrestrpos(IndexScanDesc scan)
+ {
+ 	elog(ERROR, "Bloom does not support mark/restore");
+ }
+ 
+ int64
+ blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
+ {
+ 	int64					ntids = 0;
+ 	BlockNumber				blkno = BLOOM_HEAD_BLKNO,
+ 							npages;
+ 	int						i;
+ 	BufferAccessStrategy	bas;
+ 	BloomScanOpaque 		so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so->sign == NULL && scan->numberOfKeys > 0)
+ 	{
+ 		/* New search: have to calculate search signature */
+ 		ScanKey skey = scan->keyData;
+ 
+ 		so->sign = palloc0(sizeof(SignType) * so->state.opts->bloomLength); 
+ 		
+ 		for(i = 0; i < scan->numberOfKeys; i++)
+ 		{
+ 			/*
+ 			 * Assume bloom-indexable operators to be strict, so nothing
+ 			 * could be found for NULL key.
+ 			 */
+ 			if (skey->sk_flags & SK_ISNULL)
+ 			{
+ 				pfree(so->sign);
+ 				so->sign = NULL;
+ 				return 0;
+ 			}
+ 
+ 			/* Add next value to the signature */
+ 			signValue(&so->state, so->sign, skey->sk_argument,
+ 					  skey->sk_attno - 1);
+ 
+ 			skey++;
+ 		}
+ 	}
+ 
+ 	/*
+ 	 * We're going to read the whole index. This is why we use appropriate
+ 	 * buffer access strategy.
+ 	 */
+ 	bas = GetAccessStrategy(BAS_BULKREAD);
+ 	npages = RelationGetNumberOfBlocks(scan->indexRelation);
+ 
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		Buffer 			buffer;
+ 		Page			page;
+ 
+ 		buffer = ReadBufferExtended(scan->indexRelation, MAIN_FORKNUM,
+ 									blkno, RBM_NORMAL, bas);
+ 
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 		page = BufferGetPage(buffer);
+ 
+ 		if (!BloomPageIsDeleted(page))
+ 		{
+ 			OffsetNumber offset, maxOffset = BloomPageGetMaxOffset(page);
+ 
+ 			for (offset = 1; offset <= maxOffset; offset++)
+ 			{
+ 				BloomTuple *itup = BloomPageGetTuple(&so->state, page, offset);
+ 				bool		res = true;
+ 
+ 				/* Check index signature with scan signature */
+ 				for (i = 0; res && i < so->state.opts->bloomLength; i++)
+ 				{
+ 					if ((itup->sign[i] & so->sign[i]) != so->sign[i])
+ 						res = false;
+ 				}
+ 
+ 				/* Add matching tuples to bitmap */
+ 				if (res)
+ 				{
+ 					tbm_add_tuples(tbm, &itup->heapPtr, 1, true);
+ 					ntids++;
+ 				}
+ 			}
+ 		}
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 		CHECK_FOR_INTERRUPTS();
+ 	}
+ 	FreeAccessStrategy(bas);
+ 
+ 	return ntids;
+ }
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
new file mode 100644
index ...a7ec0c5
*** a/contrib/bloom/blutils.c
--- b/contrib/bloom/blutils.c
***************
*** 0 ****
--- 1,390 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blutils.c
+  *		Bloom index utilities.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blutils.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/amapi.h"
+ #include "access/generic_xlog.h"
+ #include "catalog/index.h"
+ #include "storage/lmgr.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "utils/memutils.h"
+ #include "access/reloptions.h"
+ #include "storage/freespace.h"
+ #include "storage/indexfsm.h"
+ 
+ #include "bloom.h"
+ 
+ /* Signature dealing macros */
+ #define BITSIGNTYPE	(BITS_PER_BYTE * sizeof(SignType))
+ #define GETWORD(x,i) ( *( (SignType*)(x) + (int)( (i) / BITSIGNTYPE ) ) )
+ #define CLRBIT(x,i)   GETWORD(x,i) &= ~( 0x01 << ( (i) % BITSIGNTYPE ) )
+ #define SETBIT(x,i)   GETWORD(x,i) |=  ( 0x01 << ( (i) % BITSIGNTYPE ) )
+ #define GETBIT(x,i) ( (GETWORD(x,i) >> ( (i) % BITSIGNTYPE )) & 0x01 )
+ 
+ PG_FUNCTION_INFO_V1(blhandler);
+ 
+ static relopt_kind bl_relopt_kind;
+ 
+ /*
+  * Module initialize function: initilized relation options.
+  */
+ void 
+ _PG_init(void)
+ {
+ 	int		i;
+ 	char	buf[16];
+ 
+ 	bl_relopt_kind = add_reloption_kind();
+ 
+ 	add_int_reloption(bl_relopt_kind, "length",
+ 					  "Length of signature in uint16 type", 5, 1, 256);
+ 
+ 	for(i = 0; i < INDEX_MAX_KEYS;i ++)
+ 	{
+ 		snprintf(buf, 16, "col%d", i+1);
+ 		add_int_reloption(bl_relopt_kind, buf,
+ 						  "Number of bits for corresponding column", 2, 1, 2048);
+ 	}
+ }
+ 
+ /*
+  * Bloom handler function: return IndexAmRoutine with access method parameters
+  * and callbacks.
+  */
+ Datum
+ blhandler(PG_FUNCTION_ARGS)
+ {
+ 	IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
+ 
+ 	amroutine->amstrategies = 1;
+ 	amroutine->amsupport = 1;
+ 	amroutine->amcanorder = false;
+ 	amroutine->amcanorderbyop = false;
+ 	amroutine->amcanbackward = false;
+ 	amroutine->amcanunique = false;
+ 	amroutine->amcanmulticol = true;
+ 	amroutine->amoptionalkey = true;
+ 	amroutine->amsearcharray = false;
+ 	amroutine->amsearchnulls = false;
+ 	amroutine->amstorage = false;
+ 	amroutine->amclusterable = false;
+ 	amroutine->ampredlocks = false;
+ 	amroutine->amkeytype = 0;
+ 
+ 	amroutine->aminsert = blinsert;
+ 	amroutine->ambeginscan = blbeginscan;
+ 	amroutine->amgettuple = NULL;
+ 	amroutine->amgetbitmap = blgetbitmap;
+ 	amroutine->amrescan = blrescan;
+ 	amroutine->amendscan = blendscan;
+ 	amroutine->ammarkpos = blmarkpos;
+ 	amroutine->amrestrpos = blrestrpos;
+ 	amroutine->ambuild = blbuild;
+ 	amroutine->ambuildempty = blbuildempty;
+ 	amroutine->ambulkdelete = blbulkdelete;
+ 	amroutine->amvacuumcleanup = blvacuumcleanup;
+ 	amroutine->amcanreturn = NULL;
+ 	amroutine->amcostestimate = blcostestimate;
+ 	amroutine->amoptions = bloptions;
+ 	amroutine->amvalidate = blvalidate;
+ 
+ 	PG_RETURN_POINTER(amroutine);
+ }
+ 
+ void 
+ initBloomState(BloomState *state, Relation index)
+ {
+ 	int	i;
+ 
+ 	state->nColumns = index->rd_att->natts;
+ 
+ 	/* Initialize hash function for each attribute */
+ 	for (i = 0; i < index->rd_att->natts; i++)
+ 	{
+ 		fmgr_info_copy(&(state->hashFn[i]),
+ 						index_getprocinfo(index, i + 1, BLOOM_HASH_PROC),
+ 						CurrentMemoryContext);
+ 	}
+ 
+ 	/* Inititalize amcache if needed */
+ 	if (!index->rd_amcache)
+ 	{
+ 		Buffer				buffer;
+ 		Page				page;
+ 		BloomMetaPageData	*meta;
+ 		BloomOptions		*opts;
+ 
+ 		opts = MemoryContextAlloc(index->rd_indexcxt, sizeof(BloomOptions));
+ 
+ 		buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 
+ 		page = BufferGetPage(buffer);
+ 
+ 		if (!BloomPageIsMeta(page))
+ 			elog(ERROR, "Relation is not a bloom index");
+ 		meta = BloomPageGetMeta(BufferGetPage(buffer));
+ 
+ 		if (meta->magickNumber != BLOOM_MAGICK_NUMBER)
+ 			elog(ERROR, "Relation is not a bloom index");
+ 
+ 		*opts = meta->opts;
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 
+ 		index->rd_amcache = (void *)opts;
+ 	}
+ 
+ 	state->opts = (BloomOptions *)index->rd_amcache;
+ 	state->sizeOfBloomTuple = BLOOMTUPLEHDRSZ +
+ 									sizeof(SignType) * state->opts->bloomLength; 
+ }
+ 
+ /*
+  * Add bits of given value to the signature.
+  */
+ void
+ signValue(BloomState *state, SignType *sign, Datum value, int attno)
+ {
+ 	uint32		hashVal;
+ 	int 		nBit, j;
+ 
+ 	/*
+ 	 * init generator with "column's" number to get
+ 	 * "hashed" seed for new value. We don't want to map
+ 	 * the same numbers from different columns into the same bits!
+ 	 */
+ 	srand(attno);
+ 
+ 	/*
+ 	 * Init hash sequence to map our value into bits. the same values
+ 	 * in different columns will be mapped into different bits because
+ 	 * of step above
+ 	 */
+ 	hashVal = DatumGetInt32(FunctionCall1(&state->hashFn[attno], value));
+ 	srand(hashVal ^ rand());
+ 
+ 	for (j = 0; j < state->opts->bitSize[attno]; j++)
+ 	{
+ 		/* prevent mutiple evaluation */
+ 		nBit = rand() % (state->opts->bloomLength * BITSIGNTYPE); 
+ 		SETBIT(sign, nBit);
+ 	}
+ }
+ 
+ /*
+  * Make bloom tuple from values.
+  */
+ BloomTuple *
+ BloomFormTuple(BloomState *state, ItemPointer iptr, Datum *values, bool *isnull)
+ {
+ 	int 		i;
+ 	BloomTuple *res = (BloomTuple *)palloc0(state->sizeOfBloomTuple);
+ 
+ 	res->heapPtr = *iptr;
+ 
+     /*
+ 	 * Blooming
+ 	 */
+ 	for (i = 0; i < state->nColumns; i++)
+ 	{
+ 		/* skip nulls */
+ 		if (isnull[i])
+ 			continue;
+ 
+ 		signValue(state, res->sign, values[i], i);
+ 	}
+ 
+ 	return res;
+ }
+ 
+ /*
+  * Add new bloom tuple to the page.
+  */
+ bool
+ BloomPageAddItem(BloomState *state, Page page, BloomTuple *tuple)
+ {
+ 	BloomTuple		   *itup;
+ 	BloomPageOpaque		opaque;
+ 	Pointer				ptr;
+ 
+ 	if (BloomPageGetFreeSpace(state, page) < state->sizeOfBloomTuple)
+ 		return false;
+ 
+ 	/* Copy new tuple to the end of page */
+ 	opaque = BloomPageGetOpaque(page);
+ 	itup = BloomPageGetTuple(state, page, opaque->maxoff + 1);
+ 	memcpy((Pointer)itup, (Pointer)tuple, state->sizeOfBloomTuple);
+ 
+ 	/* Adjust maxoff and pd_lower */
+ 	opaque->maxoff++;
+ 	ptr = (Pointer)BloomPageGetTuple(state, page, opaque->maxoff + 1);
+ 	((PageHeader) page)->pd_lower = ptr - page;
+ 
+ 	return true;
+ }
+ 
+ /*
+  * Allocate a new page (either by recycling, or by extending the index file)
+  * The returned buffer is already pinned and exclusive-locked
+  * Caller is responsible for initializing the page by calling BloomInitBuffer
+  */
+ Buffer
+ BloomNewBuffer(Relation index)
+ {
+ 	Buffer      buffer;
+ 	bool        needLock;
+ 
+ 	/* First, try to get a page from FSM */
+ 	for (;;)
+ 	{
+ 		BlockNumber blkno = GetFreeIndexPage(index);
+ 
+ 		if (blkno == InvalidBlockNumber)
+ 			break;
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 
+ 		/*
+ 		 * We have to guard against the possibility that someone else already
+ 		 * recycled this page; the buffer may be locked if so.
+ 		 */
+ 		if (ConditionalLockBuffer(buffer))
+ 		{
+ 			Page	page = BufferGetPage(buffer);
+ 
+ 			if (PageIsNew(page))
+ 				return buffer;  /* OK to use, if never initialized */
+ 
+ 			if (BloomPageIsDeleted(page))
+ 				return buffer;  /* OK to use */
+ 
+ 			LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ 		}
+ 
+ 		/* Can't use it, so release buffer and try again */
+ 		ReleaseBuffer(buffer);
+ 	}
+ 
+ 	/* Must extend the file */
+ 	needLock = !RELATION_IS_LOCAL(index);
+ 	if (needLock)
+ 		LockRelationForExtension(index, ExclusiveLock);
+ 
+ 	buffer = ReadBuffer(index, P_NEW);
+ 	LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 	if (needLock)
+ 		UnlockRelationForExtension(index, ExclusiveLock);
+ 
+ 	return buffer;
+ }
+ 
+ /*
+  * Initialize bloom page.
+  */
+ void
+ BloomInitPage(Page page, uint16 flags)
+ {
+ 	BloomPageOpaque opaque;
+ 
+ 	PageInit(page, BLCKSZ, sizeof(BloomPageOpaqueData));
+ 
+ 	opaque = BloomPageGetOpaque(page);
+ 	memset(opaque, 0, sizeof(BloomPageOpaqueData));
+ 	opaque->maxoff = 0;
+ 	opaque->flags = flags;
+ }
+ 
+ static BloomOptions *
+ makeDefaultBloomOptions(BloomOptions *opts)
+ {
+ 	int i;
+ 
+ 	if (!opts)
+ 		opts = palloc0(sizeof(BloomOptions));
+ 
+ 	if (opts->bloomLength <= 0)
+ 		opts->bloomLength = 5;
+ 
+ 	for(i = 0; i < INDEX_MAX_KEYS; i++)
+ 		if (opts->bitSize[i] <= 0
+ 				|| opts->bitSize[i] >= opts->bloomLength * sizeof(SignType))
+ 			opts->bitSize[i] = 2;
+ 
+ 	return opts;
+ }
+ 
+ /*
+  * Initialize metapage for bloom index.
+  */
+ void
+ BloomInitMetapage(Relation index)
+ {
+ 	Page				metaPage;
+ 	Buffer				metaBuffer;
+ 	BloomMetaPageData  *metadata;
+ 
+ 	metaBuffer = BloomNewBuffer(index);
+ 	Assert(BufferGetBlockNumber(metaBuffer) == BLOOM_METAPAGE_BLKNO);
+ 
+ 	GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(metaBuffer, true);
+ 
+ 	BloomInitPage(metaPage, BLOOM_META);
+ 	metadata = BloomPageGetMeta(metaPage);
+ 	memset(metadata, 0, sizeof(BloomMetaPageData));
+ 	metadata->magickNumber = BLOOM_MAGICK_NUMBER;
+ 	metadata->opts = *makeDefaultBloomOptions((BloomOptions*)index->rd_options);
+ 	((PageHeader) metaPage)->pd_lower += sizeof(BloomMetaPageData);
+ 
+ 	GenericXLogFinish();
+ 	UnlockReleaseBuffer(metaBuffer);
+ }
+ 
+ /*
+  * Initialize options for bloom index.
+  */
+ bytea *
+ bloptions(Datum reloptions, bool validate)
+ {
+ 	relopt_value	   *options;
+ 	int					numoptions;
+ 	BloomOptions	   *rdopts;
+ 	relopt_parse_elt	tab[INDEX_MAX_KEYS + 1];
+ 	int					i;
+ 	char				buf[16];
+ 
+ 	tab[0].optname = "length";
+ 	tab[0].opttype = RELOPT_TYPE_INT;
+ 	tab[0].offset = offsetof(BloomOptions, bloomLength);
+ 
+ 	for(i = 0; i < INDEX_MAX_KEYS; i++)
+ 	{
+ 		snprintf(buf, sizeof(buf), "col%d", i + 1);
+ 		tab[i + 1].optname = pstrdup(buf);
+ 		tab[i + 1].opttype = RELOPT_TYPE_INT;
+ 		tab[i + 1].offset = offsetof(BloomOptions, bitSize[i]);
+ 	}
+ 
+ 	options = parseRelOptions(reloptions, validate, bl_relopt_kind, &numoptions);
+ 	rdopts = allocateReloptStruct(sizeof(BloomOptions), options, numoptions);
+ 	fillRelOptions((void *) rdopts, sizeof(BloomOptions), options, numoptions,
+ 						validate, tab, INDEX_MAX_KEYS + 1);
+ 		
+ 	rdopts = makeDefaultBloomOptions(rdopts);
+ 
+ 	return (bytea *)rdopts;
+ }
diff --git a/contrib/bloom/blvacuum.c b/contrib/bloom/blvacuum.c
new file mode 100644
index ...c694714
*** a/contrib/bloom/blvacuum.c
--- b/contrib/bloom/blvacuum.c
***************
*** 0 ****
--- 1,195 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blvacuum.c
+  *		Bloom VACUUM functions.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blvacuum.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "catalog/storage.h"
+ #include "commands/vacuum.h"
+ #include "miscadmin.h"
+ #include "postmaster/autovacuum.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "storage/lmgr.h"
+ 
+ #include "bloom.h"
+ 
+ IndexBulkDeleteResult *
+ blbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
+ 			 IndexBulkDeleteCallback callback, void *callback_state)
+ {
+ 	Relation				index = info->index;
+ 	BlockNumber				blkno,
+ 							npages;
+ 	FreeBlockNumberArray	notFullPage;
+ 	int						countPage = 0;
+ 	BloomState				state;
+ 	Buffer					buffer;
+ 	Page					page;
+ 
+ 	if (stats == NULL)
+ 		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+ 
+ 	initBloomState(&state, index); 
+ 
+ 	/*
+ 	 * Interate over the pages. We don't care about concurrently added pages,
+ 	 * they can't contain tuples to delete.
+ 	 */
+ 	npages = RelationGetNumberOfBlocks(index);
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		BloomTuple *itup, *itupPtr, *itupEnd;
+ 
+ 		buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
+ 									RBM_NORMAL, info->strategy);
+ 
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		GenericXLogStart(index);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		if (BloomPageIsDeleted(page))
+ 		{
+ 			UnlockReleaseBuffer(buffer);
+ 			CHECK_FOR_INTERRUPTS();
+ 			continue;
+ 		}
+ 
+ 		/* Iterate over the tuples */
+ 		itup = BloomPageGetTuple(&state, page, 1);
+ 		itupPtr = BloomPageGetTuple(&state, page, 1);
+ 		itupEnd = BloomPageGetTuple(&state, page, BloomPageGetMaxOffset(page) + 1);
+ 		while (itup < itupEnd)
+ 		{
+ 			/* Do we have to delete this tuple? */
+ 			if (callback(&itup->heapPtr, callback_state))
+ 			{
+ 				stats->tuples_removed += 1;
+ 				BloomPageGetOpaque(page)->maxoff--;
+ 			} 
+ 			else 
+ 			{
+ 				if (itupPtr != itup)
+ 				{
+ 					/*
+ 					 * If we already delete something before, we have to move
+ 					 * this tuple backward.
+ 					 */
+ 					memmove((Pointer)itupPtr, (Pointer)itup,
+ 							state.sizeOfBloomTuple);
+ 				}
+ 				stats->num_index_tuples++;
+ 				itupPtr = BloomPageGetNextTuple(&state, itupPtr);
+ 			}
+ 
+ 			itup = BloomPageGetNextTuple(&state, itup);
+ 		}
+ 
+ 		/* Did we delete something? */
+ 		if (itupPtr != itup)
+ 		{
+ 			/* Is it empty page now? */
+ 			if (itupPtr == BloomPageGetData(page))
+ 				BloomPageSetDeleted(page);
+ 			/* Adjust pg_lower */
+ 			((PageHeader) page)->pd_lower = (Pointer)itupPtr - page;
+ 			/* Finish WAL-logging */
+ 			GenericXLogFinish();
+ 		}
+ 		else
+ 		{
+ 			/* Didn't change anything: abort WAL-logging */
+ 			GenericXLogAbort();
+ 		}
+ 
+ 		if (!BloomPageIsDeleted(page) && 
+ 				BloomPageGetFreeSpace(&state, page) > state.sizeOfBloomTuple && 
+ 				countPage < BloomMetaBlockN)
+ 			notFullPage[countPage++] = blkno;
+ 		UnlockReleaseBuffer(buffer);
+ 		CHECK_FOR_INTERRUPTS();
+ 	}
+ 
+ 	if (countPage > 0)
+ 	{
+ 		BloomMetaPageData	*metaData;
+ 
+ 		buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 		GenericXLogStart(index);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		metaData = BloomPageGetMeta(page);
+ 		memcpy(metaData->notFullPage, notFullPage, sizeof(FreeBlockNumberArray));
+ 		metaData->nStart=0;
+ 		metaData->nEnd = countPage;
+ 
+ 		GenericXLogFinish();
+ 		UnlockReleaseBuffer(buffer);
+ 	}
+ 
+ 	return stats;
+ }
+ 
+ IndexBulkDeleteResult *
+ blvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
+ {
+ 	Relation	index = info->index;
+ 	BlockNumber npages,
+ 				blkno;
+ 	BlockNumber	totFreePages;
+ 
+ 	if (info->analyze_only)
+ 		return stats;
+ 
+ 	if (stats == NULL)
+ 		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+ 
+ 	/*
+ 	 * Iterate over the pages: insert deleted pages into FSM and collect
+ 	 * statistics.
+ 	 */
+ 	npages = RelationGetNumberOfBlocks(index);
+ 	totFreePages = 0;
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		Buffer		buffer;
+ 		Page		page;
+ 
+ 		vacuum_delay_point();
+ 
+ 		buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
+ 									RBM_NORMAL, info->strategy);
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 		page = (Page) BufferGetPage(buffer);
+ 
+ 		if (BloomPageIsDeleted(page))
+ 		{
+ 			RecordFreeIndexPage(index, blkno);
+ 			totFreePages++;
+ 		}
+ 		else
+ 		{
+ 			stats->num_index_tuples += BloomPageGetMaxOffset(page);
+ 			stats->estimated_count += BloomPageGetMaxOffset(page);
+ 		}
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 	}
+ 
+ 	IndexFreeSpaceMapVacuum(info->index);
+ 	stats->pages_free = totFreePages;
+ 	stats->num_pages = RelationGetNumberOfBlocks(index);
+ 
+ 	return stats;
+ }
diff --git a/contrib/bloom/blvalidate.c b/contrib/bloom/blvalidate.c
new file mode 100644
index ...5344b81
*** a/contrib/bloom/blvalidate.c
--- b/contrib/bloom/blvalidate.c
***************
*** 0 ****
--- 1,220 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blvalidate.c
+  *	  Opclass validator for bloom.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blvalidate.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/amvalidate.h"
+ #include "access/htup_details.h"
+ #include "catalog/pg_amop.h"
+ #include "catalog/pg_amproc.h"
+ #include "catalog/pg_opclass.h"
+ #include "catalog/pg_opfamily.h"
+ #include "catalog/pg_type.h"
+ #include "utils/builtins.h"
+ #include "utils/lsyscache.h"
+ #include "utils/syscache.h"
+ 
+ #include "bloom.h"
+ 
+ /*
+  * Validator for a bloom opclass.
+  */
+ bool
+ blvalidate(Oid opclassoid)
+ {
+ 	bool		result = true;
+ 	HeapTuple	classtup;
+ 	Form_pg_opclass classform;
+ 	Oid			opfamilyoid;
+ 	Oid			opcintype;
+ 	Oid			opckeytype;
+ 	char	   *opclassname;
+ 	HeapTuple	familytup;
+ 	Form_pg_opfamily familyform;
+ 	char	   *opfamilyname;
+ 	CatCList   *proclist,
+ 			   *oprlist;
+ 	List	   *grouplist;
+ 	OpFamilyOpFuncGroup *opclassgroup;
+ 	int			i;
+ 	ListCell   *lc;
+ 
+ 	/* Fetch opclass information */
+ 	classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
+ 	if (!HeapTupleIsValid(classtup))
+ 		elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
+ 	classform = (Form_pg_opclass) GETSTRUCT(classtup);
+ 
+ 	opfamilyoid = classform->opcfamily;
+ 	opcintype = classform->opcintype;
+ 	opckeytype = classform->opckeytype;
+ 	if (!OidIsValid(opckeytype))
+ 		opckeytype = opcintype;
+ 	opclassname = NameStr(classform->opcname);
+ 
+ 	/* Fetch opfamily information */
+ 	familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
+ 	if (!HeapTupleIsValid(familytup))
+ 		elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
+ 	familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
+ 
+ 	opfamilyname = NameStr(familyform->opfname);
+ 
+ 	/* Fetch all operators and support functions of the opfamily */
+ 	oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
+ 	proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
+ 
+ 	/* Check individual support functions */
+ 	for (i = 0; i < proclist->n_members; i++)
+ 	{
+ 		HeapTuple	proctup = &proclist->members[i]->tuple;
+ 		Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
+ 		bool		ok;
+ 
+ 		/*
+ 		 * All bloom support functions should be registered with matching
+ 		 * left/right types
+ 		 */
+ 		if (procform->amproclefttype != procform->amprocrighttype)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains support procedure %s with cross-type registration",
+ 							opfamilyname,
+ 							format_procedure(procform->amproc))));
+ 			result = false;
+ 		}
+ 
+ 		/*
+ 		 * We can't check signatures except within the specific opclass, since
+ 		 * we need to know the associated opckeytype in many cases.
+ 		 */
+ 		if (procform->amproclefttype != opcintype)
+ 			continue;
+ 
+ 		/* Check procedure numbers and function signatures */
+ 		switch (procform->amprocnum)
+ 		{
+ 			case BLOOM_HASH_PROC:
+ 				ok = check_amproc_signature(procform->amproc, INT4OID, false,
+ 											1, 1, opckeytype);
+ 				break;
+ 			default:
+ 				ereport(INFO,
+ 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 						 errmsg("bloom opfamily %s contains function %s with invalid support number %d",
+ 								opfamilyname,
+ 								format_procedure(procform->amproc),
+ 								procform->amprocnum)));
+ 				result = false;
+ 				continue;		/* don't want additional message */
+ 		}
+ 
+ 		if (!ok)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("gist opfamily %s contains function %s with wrong signature for support number %d",
+ 							opfamilyname,
+ 							format_procedure(procform->amproc),
+ 							procform->amprocnum)));
+ 			result = false;
+ 		}
+ 	}
+ 
+ 	/* Check individual operators */
+ 	for (i = 0; i < oprlist->n_members; i++)
+ 	{
+ 		HeapTuple	oprtup = &oprlist->members[i]->tuple;
+ 		Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
+ 
+ 		/* Check it's allowed strategy for bloom */
+ 		if (oprform->amopstrategy < 1 || 
+ 			oprform->amopstrategy > BLOOM_NSTRATEGIES)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains operator %s with invalid strategy number %d",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr),
+ 							oprform->amopstrategy)));
+ 			result = false;
+ 		}
+ 
+ 		/* bloom doesn't support ORDER BY operators */
+ 		if (oprform->amoppurpose != AMOP_SEARCH ||
+ 			OidIsValid(oprform->amopsortfamily))
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains invalid ORDER BY specification for operator %s",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr))));
+ 			result = false;
+ 		}
+ 
+ 		/* Check operator signature --- same for all bloom strategies */
+ 		if (!check_amop_signature(oprform->amopopr, BOOLOID,
+ 								  oprform->amoplefttype,
+ 								  oprform->amoprighttype))
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains operator %s with wrong signature",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr))));
+ 			result = false;
+ 		}
+ 	}
+ 
+ 	/* Now check for inconsistent groups of operators/functions */
+ 	grouplist = identify_opfamily_groups(oprlist, proclist);
+ 	opclassgroup = NULL;
+ 	foreach(lc, grouplist)
+ 	{
+ 		OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
+ 
+ 		/* Remember the group exactly matching the test opclass */
+ 		if (thisgroup->lefttype == opcintype &&
+ 			thisgroup->righttype == opcintype)
+ 			opclassgroup = thisgroup;
+ 
+ 		/*
+ 		 * There is not a lot we can do to check the operator sets, since each
+ 		 * bloom opclass is more or less a law unto itself, and some contain
+ 		 * only operators that are binary-compatible with the opclass datatype
+ 		 * (meaning that empty operator sets can be OK).  That case also means
+ 		 * that we shouldn't insist on nonempty function sets except for the
+ 		 * opclass's own group.
+ 		 */
+ 	}
+ 
+ 	/* Check that the originally-named opclass is complete */
+ 	for (i = 1; i <= BLOOM_NPROC; i++)
+ 	{
+ 		if (opclassgroup &&
+ 			(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
+ 			continue;			/* got it */
+ 		ereport(INFO,
+ 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 				 errmsg("bloom opclass %s is missing support function %d",
+ 						opclassname, i)));
+ 		result = false;
+ 	}
+ 
+ 	ReleaseCatCacheList(proclist);
+ 	ReleaseCatCacheList(oprlist);
+ 	ReleaseSysCache(familytup);
+ 	ReleaseSysCache(classtup);
+ 
+ 	return result;
+ }
diff --git a/contrib/bloom/data/data b/contrib/bloom/data/data
new file mode 100644
index ...eacf3e7
*** a/contrib/bloom/data/data
--- b/contrib/bloom/data/data
***************
*** 0 ****
--- 1,10000 ----
+ 739	3
+ 475	9
+ 45	6
+ 433	1
+ 948	8
+ 926	8
+ 397	7
+ 980	4
+ 212	5
+ 522	9
+ 74	8
+ 77	4
+ 378	9
+ 575	3
+ 625	2
+ 407	4
+ 509	9
+ 252	6
+ 487	7
+ 656	4
+ 485	6
+ 275	9
+ 285	3
+ 277	5
+ 804	8
+ 424	9
+ 553	5
+ 245	9
+ 384	8
+ 202	0
+ 43	6
+ 374	6
+ 490	6
+ 105	10
+ 311	8
+ 411	8
+ 343	7
+ 678	6
+ 942	10
+ 126	7
+ 755	6
+ 625	3
+ 52	9
+ 239	4
+ 690	4
+ 445	7
+ 49	8
+ 285	5
+ 445	4
+ 516	8
+ 151	9
+ 553	5
+ 613	2
+ 123	6
+ 187	2
+ 301	9
+ 800	9
+ 250	9
+ 796	5
+ 288	5
+ 930	7
+ 219	10
+ 553	5
+ 518	10
+ 893	0
+ 754	0
+ 960	3
+ 538	6
+ 537	7
+ 127	7
+ 910	4
+ 666	7
+ 354	9
+ 562	1
+ 405	8
+ 635	3
+ 583	9
+ 313	1
+ 358	8
+ 133	3
+ 864	9
+ 296	8
+ 193	8
+ 396	7
+ 495	5
+ 454	4
+ 952	1
+ 115	3
+ 36	7
+ 455	4
+ 527	1
+ 775	1
+ 945	1
+ 246	3
+ 920	4
+ 554	8
+ 267	8
+ 608	5
+ 684	0
+ 190	2
+ 527	6
+ 584	5
+ 764	7
+ 785	8
+ 376	2
+ 240	9
+ 330	0
+ 13	3
+ 103	3
+ 578	0
+ 639	1
+ 807	9
+ 982	4
+ 365	7
+ 418	6
+ 844	9
+ 199	4
+ 425	10
+ 127	2
+ 762	5
+ 450	0
+ 406	8
+ 17	4
+ 55	1
+ 678	6
+ 143	3
+ 764	9
+ 222	7
+ 364	6
+ 411	8
+ 142	3
+ 728	3
+ 684	2
+ 303	8
+ 363	1
+ 314	8
+ 67	7
+ 592	1
+ 139	6
+ 204	8
+ 279	3
+ 133	0
+ 296	4
+ 788	7
+ 942	2
+ 441	1
+ 454	2
+ 424	1
+ 322	7
+ 949	7
+ 793	3
+ 497	9
+ 984	1
+ 944	1
+ 736	1
+ 940	0
+ 494	1
+ 57	8
+ 429	8
+ 449	4
+ 44	9
+ 454	5
+ 60	9
+ 636	4
+ 606	6
+ 67	4
+ 848	6
+ 259	8
+ 654	2
+ 955	4
+ 351	9
+ 405	8
+ 968	5
+ 634	4
+ 308	1
+ 767	4
+ 974	2
+ 850	0
+ 99	5
+ 416	7
+ 71	5
+ 103	9
+ 48	4
+ 750	7
+ 565	7
+ 92	9
+ 599	5
+ 760	6
+ 960	4
+ 964	3
+ 478	7
+ 620	5
+ 953	5
+ 485	1
+ 957	9
+ 756	0
+ 383	9
+ 946	4
+ 222	7
+ 133	8
+ 401	2
+ 702	0
+ 723	5
+ 568	7
+ 857	5
+ 951	3
+ 264	6
+ 786	2
+ 42	3
+ 268	10
+ 172	0
+ 26	6
+ 884	10
+ 986	1
+ 667	1
+ 893	1
+ 344	6
+ 68	1
+ 58	6
+ 750	9
+ 168	7
+ 249	4
+ 273	0
+ 649	3
+ 306	9
+ 314	5
+ 942	3
+ 33	8
+ 311	0
+ 932	10
+ 138	8
+ 47	5
+ 420	1
+ 550	5
+ 751	3
+ 392	9
+ 1	6
+ 351	3
+ 677	10
+ 588	10
+ 917	9
+ 461	9
+ 242	5
+ 685	6
+ 514	6
+ 531	7
+ 442	6
+ 135	9
+ 693	7
+ 341	4
+ 984	7
+ 362	10
+ 375	7
+ 259	1
+ 713	8
+ 35	6
+ 750	5
+ 489	10
+ 991	2
+ 544	5
+ 791	1
+ 156	2
+ 653	3
+ 96	3
+ 976	4
+ 789	10
+ 170	2
+ 946	5
+ 865	2
+ 597	6
+ 53	6
+ 209	8
+ 128	7
+ 794	1
+ 871	3
+ 623	7
+ 413	8
+ 895	1
+ 70	10
+ 411	0
+ 428	2
+ 6	6
+ 352	10
+ 143	2
+ 156	7
+ 795	2
+ 372	0
+ 11	5
+ 701	8
+ 619	6
+ 142	2
+ 233	6
+ 20	1
+ 621	1
+ 118	0
+ 136	5
+ 232	1
+ 145	6
+ 93	3
+ 800	2
+ 28	6
+ 457	4
+ 598	5
+ 900	3
+ 273	5
+ 870	4
+ 760	1
+ 970	8
+ 231	6
+ 871	3
+ 622	0
+ 895	9
+ 148	0
+ 439	2
+ 328	2
+ 489	4
+ 834	9
+ 756	4
+ 415	7
+ 730	7
+ 175	6
+ 102	9
+ 703	1
+ 715	9
+ 662	6
+ 283	3
+ 592	2
+ 139	7
+ 218	6
+ 981	5
+ 816	5
+ 902	7
+ 417	7
+ 82	8
+ 314	8
+ 519	5
+ 413	6
+ 424	1
+ 693	1
+ 50	4
+ 725	3
+ 639	3
+ 511	8
+ 58	7
+ 356	0
+ 275	2
+ 510	2
+ 822	9
+ 835	9
+ 759	1
+ 717	3
+ 639	1
+ 899	1
+ 246	6
+ 202	3
+ 946	9
+ 629	6
+ 245	1
+ 363	3
+ 870	7
+ 342	1
+ 891	9
+ 322	7
+ 778	2
+ 617	5
+ 307	3
+ 814	9
+ 463	7
+ 8	7
+ 304	2
+ 5	2
+ 138	6
+ 835	4
+ 774	2
+ 685	6
+ 916	0
+ 789	8
+ 878	1
+ 519	7
+ 269	1
+ 193	6
+ 470	0
+ 522	9
+ 720	5
+ 643	0
+ 741	6
+ 275	9
+ 282	1
+ 261	1
+ 306	9
+ 701	2
+ 973	5
+ 28	9
+ 602	5
+ 507	9
+ 683	7
+ 448	2
+ 708	10
+ 88	4
+ 501	7
+ 453	2
+ 379	7
+ 120	7
+ 836	4
+ 717	1
+ 327	4
+ 365	3
+ 908	4
+ 151	5
+ 940	7
+ 381	6
+ 359	8
+ 777	1
+ 799	9
+ 495	3
+ 595	9
+ 542	10
+ 675	7
+ 634	5
+ 44	4
+ 654	4
+ 769	0
+ 671	7
+ 411	8
+ 187	4
+ 481	6
+ 974	8
+ 398	8
+ 907	2
+ 615	4
+ 497	2
+ 349	0
+ 183	0
+ 701	8
+ 535	7
+ 169	2
+ 116	9
+ 208	8
+ 615	6
+ 610	8
+ 970	1
+ 371	9
+ 931	8
+ 695	8
+ 966	3
+ 239	5
+ 520	6
+ 502	7
+ 612	2
+ 520	1
+ 948	7
+ 337	1
+ 627	5
+ 852	2
+ 165	5
+ 45	1
+ 554	4
+ 79	5
+ 185	8
+ 323	2
+ 84	6
+ 613	6
+ 151	1
+ 306	8
+ 318	8
+ 911	3
+ 516	2
+ 331	1
+ 793	2
+ 385	10
+ 646	4
+ 92	2
+ 846	2
+ 686	0
+ 945	0
+ 181	0
+ 572	8
+ 633	7
+ 909	9
+ 486	2
+ 766	4
+ 493	3
+ 645	8
+ 424	4
+ 8	8
+ 395	7
+ 240	5
+ 855	1
+ 659	5
+ 117	6
+ 551	3
+ 634	1
+ 93	3
+ 846	0
+ 206	3
+ 228	10
+ 730	7
+ 253	4
+ 546	7
+ 813	6
+ 487	2
+ 209	7
+ 696	1
+ 814	4
+ 605	9
+ 959	2
+ 230	6
+ 278	3
+ 860	1
+ 324	1
+ 457	6
+ 37	2
+ 273	3
+ 561	8
+ 968	4
+ 373	5
+ 582	6
+ 183	3
+ 646	10
+ 633	3
+ 928	6
+ 407	2
+ 185	7
+ 480	0
+ 810	8
+ 111	3
+ 355	1
+ 453	6
+ 439	0
+ 447	4
+ 388	8
+ 862	10
+ 402	0
+ 247	0
+ 42	9
+ 299	10
+ 472	7
+ 127	7
+ 392	6
+ 702	2
+ 410	8
+ 468	8
+ 960	9
+ 394	4
+ 935	8
+ 806	3
+ 661	7
+ 292	1
+ 714	5
+ 111	8
+ 419	4
+ 725	9
+ 117	9
+ 548	5
+ 460	2
+ 711	9
+ 62	2
+ 636	0
+ 99	0
+ 421	0
+ 870	2
+ 357	5
+ 896	6
+ 594	6
+ 189	7
+ 365	6
+ 116	1
+ 499	2
+ 943	0
+ 742	4
+ 297	5
+ 273	4
+ 631	9
+ 382	7
+ 938	8
+ 765	8
+ 30	1
+ 338	9
+ 771	9
+ 535	10
+ 637	9
+ 568	8
+ 990	1
+ 986	9
+ 114	7
+ 335	4
+ 181	6
+ 770	8
+ 517	2
+ 543	5
+ 954	3
+ 262	10
+ 430	6
+ 910	2
+ 531	4
+ 160	2
+ 345	7
+ 921	3
+ 795	9
+ 267	9
+ 635	6
+ 319	8
+ 211	1
+ 628	7
+ 240	2
+ 182	2
+ 479	4
+ 179	9
+ 43	1
+ 110	6
+ 535	3
+ 743	9
+ 999	7
+ 214	8
+ 571	5
+ 702	2
+ 85	0
+ 22	3
+ 111	6
+ 23	4
+ 821	2
+ 546	3
+ 648	7
+ 210	7
+ 814	3
+ 267	3
+ 590	0
+ 228	6
+ 674	4
+ 382	2
+ 924	1
+ 450	0
+ 106	5
+ 304	2
+ 121	3
+ 568	9
+ 532	1
+ 243	2
+ 839	5
+ 872	7
+ 772	1
+ 2	4
+ 148	2
+ 950	8
+ 673	3
+ 66	6
+ 416	5
+ 606	5
+ 987	9
+ 739	1
+ 237	3
+ 51	8
+ 422	3
+ 949	3
+ 746	8
+ 914	5
+ 959	9
+ 880	1
+ 146	8
+ 929	8
+ 163	10
+ 416	6
+ 511	0
+ 102	5
+ 932	8
+ 606	2
+ 149	7
+ 939	6
+ 951	9
+ 831	7
+ 710	7
+ 215	7
+ 662	1
+ 777	8
+ 926	7
+ 627	1
+ 701	0
+ 668	2
+ 66	8
+ 709	10
+ 611	3
+ 168	8
+ 973	1
+ 330	9
+ 996	2
+ 620	7
+ 907	8
+ 375	6
+ 931	2
+ 377	9
+ 857	0
+ 945	6
+ 49	6
+ 769	1
+ 384	5
+ 113	10
+ 794	3
+ 756	8
+ 389	1
+ 690	4
+ 248	3
+ 90	2
+ 146	5
+ 724	1
+ 616	1
+ 933	5
+ 106	9
+ 31	2
+ 492	8
+ 270	9
+ 279	4
+ 872	1
+ 664	6
+ 840	1
+ 713	5
+ 438	10
+ 841	5
+ 116	10
+ 994	8
+ 63	6
+ 942	10
+ 83	0
+ 874	1
+ 203	4
+ 914	5
+ 242	2
+ 856	1
+ 265	5
+ 742	1
+ 573	5
+ 635	0
+ 416	5
+ 540	5
+ 462	5
+ 373	5
+ 143	3
+ 520	2
+ 363	4
+ 340	6
+ 760	3
+ 40	0
+ 446	9
+ 117	7
+ 416	9
+ 816	10
+ 313	5
+ 0	7
+ 927	5
+ 261	4
+ 74	6
+ 914	2
+ 949	4
+ 443	3
+ 829	8
+ 879	6
+ 37	9
+ 591	5
+ 814	7
+ 195	2
+ 566	0
+ 218	9
+ 462	2
+ 608	4
+ 759	9
+ 778	8
+ 504	7
+ 49	5
+ 127	5
+ 765	10
+ 276	6
+ 544	3
+ 562	1
+ 797	4
+ 843	10
+ 605	4
+ 2	8
+ 288	5
+ 42	9
+ 853	8
+ 765	6
+ 633	3
+ 324	7
+ 722	5
+ 175	5
+ 406	5
+ 130	9
+ 765	7
+ 85	6
+ 68	9
+ 553	7
+ 337	6
+ 497	6
+ 19	5
+ 520	9
+ 340	3
+ 504	10
+ 554	8
+ 656	3
+ 279	8
+ 763	7
+ 283	9
+ 634	0
+ 585	7
+ 609	7
+ 647	2
+ 326	10
+ 717	8
+ 608	7
+ 362	1
+ 608	7
+ 413	1
+ 676	10
+ 940	3
+ 244	2
+ 163	0
+ 903	4
+ 899	5
+ 494	5
+ 256	1
+ 136	9
+ 264	5
+ 886	10
+ 285	5
+ 717	6
+ 621	3
+ 349	0
+ 436	0
+ 2	4
+ 356	2
+ 595	5
+ 251	5
+ 965	2
+ 34	5
+ 633	3
+ 562	8
+ 192	8
+ 231	1
+ 807	5
+ 571	5
+ 163	2
+ 848	5
+ 226	3
+ 536	2
+ 661	9
+ 473	3
+ 412	7
+ 753	4
+ 874	8
+ 837	5
+ 77	4
+ 277	3
+ 225	5
+ 347	0
+ 24	9
+ 555	2
+ 109	4
+ 699	3
+ 688	2
+ 563	3
+ 128	0
+ 604	5
+ 759	4
+ 919	6
+ 143	8
+ 141	2
+ 154	4
+ 488	4
+ 926	8
+ 410	10
+ 752	10
+ 137	9
+ 369	8
+ 197	1
+ 72	8
+ 405	2
+ 795	0
+ 741	6
+ 365	7
+ 187	5
+ 415	3
+ 728	6
+ 745	2
+ 948	7
+ 51	4
+ 621	8
+ 324	8
+ 665	7
+ 595	9
+ 750	7
+ 622	2
+ 867	4
+ 164	6
+ 971	5
+ 267	2
+ 38	7
+ 485	8
+ 251	2
+ 982	2
+ 902	0
+ 556	5
+ 836	9
+ 282	5
+ 573	9
+ 364	3
+ 543	10
+ 477	4
+ 403	6
+ 18	4
+ 171	3
+ 531	2
+ 966	0
+ 974	2
+ 247	10
+ 415	1
+ 988	10
+ 672	8
+ 851	10
+ 325	4
+ 830	7
+ 746	4
+ 675	2
+ 784	1
+ 865	8
+ 450	0
+ 86	10
+ 244	1
+ 998	2
+ 269	2
+ 173	7
+ 394	2
+ 655	1
+ 986	5
+ 20	3
+ 930	8
+ 0	7
+ 224	7
+ 900	0
+ 752	8
+ 809	2
+ 800	9
+ 184	0
+ 947	2
+ 261	2
+ 427	4
+ 899	8
+ 596	6
+ 887	6
+ 60	9
+ 894	10
+ 757	9
+ 667	10
+ 569	6
+ 987	3
+ 331	8
+ 524	1
+ 691	7
+ 174	6
+ 891	4
+ 854	3
+ 870	8
+ 139	5
+ 307	0
+ 48	4
+ 933	9
+ 358	7
+ 836	0
+ 670	4
+ 591	7
+ 726	9
+ 454	3
+ 53	1
+ 959	2
+ 783	8
+ 663	6
+ 168	5
+ 389	3
+ 999	7
+ 334	0
+ 64	3
+ 989	4
+ 957	8
+ 447	6
+ 231	0
+ 285	10
+ 960	7
+ 208	0
+ 883	2
+ 240	7
+ 16	9
+ 302	2
+ 435	7
+ 490	4
+ 388	8
+ 481	5
+ 91	5
+ 874	0
+ 296	3
+ 675	5
+ 359	10
+ 484	3
+ 698	7
+ 332	6
+ 858	6
+ 247	9
+ 475	5
+ 57	9
+ 241	5
+ 344	6
+ 371	8
+ 81	5
+ 296	10
+ 509	6
+ 277	2
+ 120	6
+ 143	6
+ 955	8
+ 296	3
+ 421	2
+ 860	7
+ 28	3
+ 217	1
+ 244	5
+ 632	6
+ 87	0
+ 414	2
+ 465	7
+ 123	10
+ 303	4
+ 158	4
+ 36	3
+ 27	10
+ 142	3
+ 278	6
+ 476	1
+ 231	5
+ 472	4
+ 588	7
+ 907	2
+ 305	10
+ 223	7
+ 161	7
+ 428	3
+ 662	7
+ 684	8
+ 154	7
+ 121	2
+ 711	3
+ 503	10
+ 826	10
+ 127	1
+ 483	6
+ 506	1
+ 316	4
+ 292	6
+ 407	5
+ 339	6
+ 203	8
+ 853	9
+ 499	5
+ 684	7
+ 257	8
+ 833	10
+ 68	3
+ 958	9
+ 316	1
+ 950	8
+ 685	5
+ 870	0
+ 869	2
+ 622	3
+ 676	10
+ 844	9
+ 729	7
+ 743	2
+ 234	4
+ 881	5
+ 233	7
+ 460	3
+ 51	4
+ 193	4
+ 503	1
+ 165	2
+ 600	0
+ 189	5
+ 197	8
+ 745	9
+ 773	6
+ 752	5
+ 285	5
+ 730	5
+ 923	6
+ 10	2
+ 324	5
+ 455	4
+ 888	6
+ 741	4
+ 792	9
+ 579	4
+ 942	8
+ 862	1
+ 580	6
+ 12	4
+ 196	8
+ 854	5
+ 259	6
+ 1	2
+ 195	0
+ 336	5
+ 481	8
+ 894	4
+ 440	6
+ 760	2
+ 542	3
+ 625	5
+ 107	5
+ 622	7
+ 94	6
+ 40	3
+ 397	9
+ 771	7
+ 479	8
+ 837	7
+ 783	2
+ 192	3
+ 964	1
+ 633	4
+ 721	4
+ 637	3
+ 732	3
+ 746	8
+ 749	4
+ 527	8
+ 2	6
+ 133	4
+ 462	9
+ 54	9
+ 677	9
+ 613	5
+ 64	8
+ 725	0
+ 891	4
+ 433	6
+ 751	1
+ 876	5
+ 332	6
+ 324	1
+ 990	9
+ 925	10
+ 418	1
+ 390	9
+ 962	4
+ 820	6
+ 335	4
+ 99	4
+ 239	8
+ 427	1
+ 182	9
+ 743	9
+ 930	6
+ 418	3
+ 241	7
+ 344	2
+ 593	3
+ 223	0
+ 326	6
+ 891	3
+ 58	7
+ 928	4
+ 145	0
+ 792	4
+ 851	2
+ 514	0
+ 80	3
+ 967	0
+ 875	4
+ 272	1
+ 126	6
+ 347	7
+ 884	6
+ 730	2
+ 184	6
+ 498	2
+ 333	4
+ 635	5
+ 453	4
+ 861	3
+ 647	4
+ 338	7
+ 632	3
+ 736	5
+ 689	0
+ 624	8
+ 623	10
+ 534	5
+ 541	3
+ 717	7
+ 885	2
+ 967	2
+ 641	6
+ 696	1
+ 29	6
+ 399	7
+ 933	7
+ 403	6
+ 41	1
+ 73	7
+ 147	7
+ 546	8
+ 668	1
+ 278	2
+ 344	10
+ 934	2
+ 209	9
+ 447	8
+ 503	1
+ 944	5
+ 700	3
+ 208	6
+ 79	6
+ 198	1
+ 750	3
+ 851	9
+ 967	4
+ 668	6
+ 477	9
+ 843	8
+ 940	8
+ 51	1
+ 678	5
+ 999	2
+ 641	9
+ 713	3
+ 285	9
+ 974	4
+ 532	2
+ 485	3
+ 442	3
+ 179	4
+ 733	8
+ 44	2
+ 792	9
+ 32	7
+ 664	1
+ 880	3
+ 581	9
+ 523	2
+ 822	2
+ 563	1
+ 157	5
+ 471	7
+ 709	10
+ 971	2
+ 292	2
+ 561	0
+ 997	6
+ 236	8
+ 491	3
+ 521	2
+ 351	4
+ 498	9
+ 281	0
+ 153	1
+ 258	7
+ 209	4
+ 253	7
+ 105	10
+ 636	1
+ 113	9
+ 227	7
+ 954	2
+ 278	2
+ 14	8
+ 459	5
+ 926	8
+ 937	4
+ 742	2
+ 446	9
+ 320	7
+ 611	5
+ 120	9
+ 210	2
+ 827	8
+ 301	9
+ 775	5
+ 614	7
+ 753	9
+ 918	8
+ 663	4
+ 302	6
+ 187	2
+ 13	9
+ 457	5
+ 824	8
+ 163	4
+ 307	3
+ 300	5
+ 508	1
+ 363	8
+ 67	1
+ 338	7
+ 866	1
+ 573	8
+ 858	2
+ 161	2
+ 824	3
+ 399	8
+ 277	9
+ 295	1
+ 633	5
+ 536	9
+ 742	8
+ 456	3
+ 963	8
+ 61	0
+ 956	4
+ 710	8
+ 490	3
+ 606	3
+ 519	8
+ 508	3
+ 116	9
+ 179	4
+ 762	5
+ 494	4
+ 934	0
+ 335	7
+ 867	8
+ 926	8
+ 610	10
+ 859	6
+ 386	6
+ 389	9
+ 852	10
+ 224	4
+ 763	7
+ 713	9
+ 638	9
+ 272	4
+ 367	8
+ 796	3
+ 796	1
+ 977	7
+ 921	9
+ 493	5
+ 890	4
+ 98	3
+ 921	5
+ 152	8
+ 482	4
+ 143	2
+ 108	9
+ 124	7
+ 750	4
+ 147	1
+ 162	9
+ 418	10
+ 73	4
+ 622	10
+ 298	1
+ 526	2
+ 466	6
+ 464	4
+ 111	6
+ 159	6
+ 992	3
+ 837	1
+ 159	10
+ 847	9
+ 357	10
+ 26	5
+ 937	4
+ 478	0
+ 839	1
+ 5	1
+ 214	5
+ 325	7
+ 156	8
+ 66	3
+ 405	2
+ 859	4
+ 527	7
+ 498	7
+ 658	3
+ 595	0
+ 339	6
+ 535	3
+ 65	0
+ 286	9
+ 112	3
+ 41	3
+ 823	4
+ 6	10
+ 154	1
+ 245	6
+ 295	1
+ 957	8
+ 800	5
+ 508	5
+ 801	1
+ 473	1
+ 723	0
+ 415	8
+ 21	7
+ 691	1
+ 993	7
+ 460	8
+ 97	5
+ 795	3
+ 536	0
+ 811	8
+ 144	8
+ 654	9
+ 224	2
+ 403	0
+ 263	9
+ 165	10
+ 884	6
+ 774	9
+ 282	5
+ 39	3
+ 197	5
+ 91	3
+ 964	9
+ 546	5
+ 926	4
+ 332	1
+ 127	10
+ 15	4
+ 146	4
+ 376	4
+ 293	5
+ 396	2
+ 120	2
+ 83	4
+ 636	1
+ 677	8
+ 620	8
+ 128	6
+ 655	7
+ 84	6
+ 32	4
+ 651	2
+ 400	7
+ 510	5
+ 83	9
+ 957	4
+ 426	4
+ 554	5
+ 523	6
+ 949	2
+ 758	6
+ 992	4
+ 395	1
+ 962	0
+ 794	0
+ 630	8
+ 461	3
+ 984	9
+ 947	5
+ 408	0
+ 380	4
+ 407	8
+ 717	10
+ 352	2
+ 598	3
+ 399	4
+ 927	4
+ 734	3
+ 510	7
+ 371	3
+ 742	0
+ 129	2
+ 283	1
+ 63	2
+ 608	5
+ 261	10
+ 835	7
+ 793	6
+ 628	1
+ 793	2
+ 446	2
+ 582	4
+ 583	3
+ 695	1
+ 13	1
+ 397	8
+ 68	5
+ 957	4
+ 641	0
+ 582	2
+ 491	8
+ 235	3
+ 510	0
+ 879	1
+ 173	7
+ 365	6
+ 863	9
+ 992	4
+ 264	7
+ 540	3
+ 754	9
+ 32	8
+ 464	10
+ 174	1
+ 9	8
+ 353	5
+ 598	6
+ 827	1
+ 616	7
+ 247	8
+ 377	6
+ 407	2
+ 558	4
+ 686	8
+ 86	2
+ 99	8
+ 163	1
+ 662	6
+ 120	8
+ 731	1
+ 591	1
+ 630	2
+ 671	5
+ 298	3
+ 162	5
+ 75	5
+ 155	5
+ 779	7
+ 880	5
+ 535	10
+ 691	6
+ 806	9
+ 764	5
+ 480	9
+ 303	2
+ 13	9
+ 294	6
+ 84	10
+ 100	4
+ 252	3
+ 926	3
+ 801	1
+ 808	6
+ 794	7
+ 45	3
+ 655	7
+ 963	5
+ 589	7
+ 929	1
+ 611	2
+ 279	6
+ 127	6
+ 267	2
+ 538	4
+ 592	8
+ 629	5
+ 117	4
+ 599	9
+ 10	4
+ 614	1
+ 722	3
+ 790	7
+ 730	4
+ 413	7
+ 447	0
+ 891	7
+ 648	0
+ 299	9
+ 228	8
+ 282	8
+ 627	9
+ 338	7
+ 340	9
+ 669	3
+ 330	3
+ 404	1
+ 552	2
+ 738	3
+ 574	2
+ 941	0
+ 174	8
+ 747	8
+ 849	0
+ 738	1
+ 884	0
+ 897	5
+ 931	2
+ 256	3
+ 173	9
+ 621	5
+ 209	0
+ 556	8
+ 220	3
+ 43	8
+ 444	10
+ 815	6
+ 816	6
+ 441	7
+ 609	2
+ 742	5
+ 199	6
+ 4	1
+ 875	3
+ 400	0
+ 185	0
+ 551	4
+ 46	1
+ 155	3
+ 400	2
+ 60	8
+ 183	9
+ 463	10
+ 436	9
+ 665	0
+ 82	4
+ 538	3
+ 47	5
+ 410	9
+ 802	8
+ 970	10
+ 832	5
+ 381	9
+ 627	5
+ 145	0
+ 734	2
+ 872	9
+ 79	3
+ 916	5
+ 238	6
+ 560	3
+ 988	1
+ 602	0
+ 639	0
+ 956	4
+ 823	9
+ 429	7
+ 446	8
+ 533	1
+ 346	7
+ 101	1
+ 883	10
+ 997	10
+ 307	9
+ 477	5
+ 495	0
+ 865	5
+ 135	5
+ 517	8
+ 479	5
+ 215	3
+ 399	6
+ 957	8
+ 454	5
+ 919	8
+ 168	0
+ 880	1
+ 992	9
+ 13	3
+ 791	5
+ 844	3
+ 527	7
+ 768	7
+ 176	3
+ 435	7
+ 759	7
+ 957	2
+ 295	9
+ 4	7
+ 403	9
+ 548	6
+ 943	4
+ 622	9
+ 305	6
+ 235	1
+ 124	1
+ 381	7
+ 789	1
+ 312	10
+ 434	7
+ 619	2
+ 398	6
+ 351	7
+ 489	4
+ 442	9
+ 279	10
+ 463	2
+ 418	1
+ 158	7
+ 720	4
+ 819	8
+ 473	2
+ 496	3
+ 349	8
+ 226	8
+ 556	8
+ 976	10
+ 421	3
+ 648	9
+ 683	1
+ 803	10
+ 80	3
+ 184	5
+ 352	3
+ 221	1
+ 736	0
+ 917	2
+ 240	4
+ 470	6
+ 221	7
+ 372	8
+ 542	3
+ 731	10
+ 676	4
+ 874	4
+ 469	7
+ 321	5
+ 943	5
+ 46	3
+ 848	3
+ 367	6
+ 307	3
+ 793	5
+ 697	3
+ 135	9
+ 959	5
+ 695	5
+ 855	4
+ 464	5
+ 806	3
+ 890	3
+ 14	2
+ 822	10
+ 715	9
+ 253	6
+ 135	6
+ 147	4
+ 904	9
+ 988	6
+ 203	1
+ 519	2
+ 630	2
+ 663	5
+ 640	1
+ 16	4
+ 465	9
+ 720	5
+ 115	5
+ 437	8
+ 410	7
+ 393	5
+ 309	5
+ 987	2
+ 479	10
+ 814	7
+ 97	3
+ 844	7
+ 547	5
+ 212	2
+ 634	2
+ 634	1
+ 133	4
+ 579	2
+ 896	0
+ 79	3
+ 706	5
+ 852	0
+ 12	8
+ 228	5
+ 813	0
+ 173	9
+ 376	0
+ 637	9
+ 524	8
+ 111	2
+ 76	7
+ 258	2
+ 98	8
+ 457	10
+ 853	5
+ 301	6
+ 8	2
+ 574	0
+ 991	8
+ 511	8
+ 845	7
+ 713	2
+ 702	4
+ 144	2
+ 199	3
+ 385	3
+ 999	6
+ 483	1
+ 481	9
+ 91	3
+ 475	4
+ 893	5
+ 544	5
+ 503	5
+ 270	0
+ 338	1
+ 698	1
+ 336	4
+ 402	5
+ 626	6
+ 735	0
+ 875	7
+ 655	4
+ 830	1
+ 298	9
+ 469	8
+ 313	4
+ 256	9
+ 830	8
+ 392	1
+ 773	7
+ 215	5
+ 782	6
+ 871	2
+ 31	5
+ 784	8
+ 509	7
+ 499	2
+ 17	3
+ 299	3
+ 250	8
+ 89	6
+ 130	3
+ 421	10
+ 104	8
+ 59	9
+ 543	3
+ 348	3
+ 824	2
+ 508	9
+ 717	3
+ 620	2
+ 950	1
+ 390	10
+ 448	7
+ 282	7
+ 457	4
+ 262	6
+ 716	7
+ 546	8
+ 496	6
+ 697	0
+ 879	0
+ 363	7
+ 265	9
+ 557	10
+ 163	2
+ 209	1
+ 296	6
+ 80	7
+ 288	4
+ 442	7
+ 733	7
+ 332	4
+ 387	9
+ 269	9
+ 483	10
+ 921	4
+ 12	3
+ 64	3
+ 155	6
+ 260	3
+ 799	5
+ 431	1
+ 68	5
+ 839	4
+ 873	3
+ 101	6
+ 986	4
+ 55	4
+ 311	3
+ 255	8
+ 290	2
+ 155	3
+ 460	2
+ 579	6
+ 840	8
+ 933	6
+ 308	4
+ 735	4
+ 875	6
+ 733	7
+ 855	8
+ 353	8
+ 268	4
+ 213	6
+ 732	5
+ 372	0
+ 644	5
+ 324	1
+ 746	9
+ 718	6
+ 743	7
+ 225	1
+ 15	10
+ 428	9
+ 534	2
+ 637	4
+ 996	10
+ 230	3
+ 399	4
+ 842	1
+ 911	2
+ 153	6
+ 741	5
+ 658	5
+ 380	4
+ 72	1
+ 28	3
+ 174	0
+ 258	6
+ 933	8
+ 763	6
+ 181	8
+ 561	4
+ 22	10
+ 854	9
+ 90	8
+ 78	2
+ 320	8
+ 719	10
+ 305	1
+ 354	4
+ 222	4
+ 675	4
+ 425	9
+ 997	4
+ 725	8
+ 928	9
+ 518	5
+ 317	5
+ 447	2
+ 405	5
+ 936	5
+ 780	3
+ 302	5
+ 233	6
+ 598	6
+ 985	8
+ 969	7
+ 215	4
+ 594	2
+ 752	3
+ 973	7
+ 224	5
+ 167	5
+ 32	6
+ 712	4
+ 152	6
+ 920	9
+ 903	2
+ 430	1
+ 830	0
+ 724	8
+ 848	7
+ 477	1
+ 88	1
+ 276	8
+ 389	2
+ 519	6
+ 740	7
+ 154	8
+ 301	9
+ 209	5
+ 514	1
+ 385	4
+ 351	8
+ 553	2
+ 843	3
+ 998	7
+ 971	5
+ 754	1
+ 545	0
+ 898	9
+ 279	4
+ 547	0
+ 104	7
+ 791	4
+ 568	10
+ 858	1
+ 129	2
+ 499	5
+ 58	1
+ 662	9
+ 330	7
+ 592	3
+ 134	3
+ 359	7
+ 376	3
+ 613	7
+ 675	2
+ 674	8
+ 862	5
+ 183	4
+ 465	0
+ 512	6
+ 284	0
+ 74	3
+ 63	7
+ 244	4
+ 395	8
+ 693	5
+ 181	1
+ 208	6
+ 309	8
+ 212	10
+ 981	9
+ 763	8
+ 352	9
+ 273	8
+ 988	8
+ 410	3
+ 796	5
+ 614	9
+ 220	9
+ 251	6
+ 693	9
+ 144	9
+ 995	4
+ 432	3
+ 174	6
+ 289	2
+ 531	1
+ 998	9
+ 997	3
+ 700	10
+ 57	1
+ 257	9
+ 594	9
+ 711	8
+ 730	10
+ 429	4
+ 905	6
+ 298	9
+ 926	7
+ 205	1
+ 374	5
+ 254	9
+ 545	3
+ 788	5
+ 524	5
+ 528	6
+ 598	8
+ 433	2
+ 656	1
+ 6	4
+ 105	4
+ 809	0
+ 8	1
+ 910	9
+ 836	1
+ 34	2
+ 608	3
+ 115	2
+ 541	9
+ 696	1
+ 391	2
+ 645	10
+ 8	1
+ 180	7
+ 220	2
+ 51	3
+ 621	9
+ 335	6
+ 966	2
+ 564	8
+ 359	6
+ 12	10
+ 887	1
+ 120	4
+ 31	8
+ 492	4
+ 40	1
+ 410	0
+ 213	6
+ 713	4
+ 777	8
+ 759	4
+ 623	1
+ 28	6
+ 338	6
+ 390	7
+ 191	4
+ 663	1
+ 530	8
+ 505	6
+ 599	10
+ 983	6
+ 133	4
+ 687	3
+ 984	4
+ 780	8
+ 163	5
+ 160	8
+ 632	2
+ 374	10
+ 780	8
+ 666	10
+ 167	3
+ 48	7
+ 112	6
+ 258	7
+ 549	2
+ 350	7
+ 635	0
+ 27	6
+ 437	8
+ 380	6
+ 345	5
+ 386	10
+ 727	8
+ 947	5
+ 525	6
+ 477	7
+ 942	5
+ 389	1
+ 77	6
+ 765	6
+ 889	1
+ 308	5
+ 153	3
+ 142	6
+ 143	5
+ 191	5
+ 62	6
+ 465	8
+ 338	4
+ 296	9
+ 25	8
+ 555	10
+ 298	9
+ 20	4
+ 591	8
+ 2	5
+ 901	3
+ 3	1
+ 645	1
+ 645	8
+ 667	8
+ 276	7
+ 413	7
+ 517	8
+ 153	8
+ 613	2
+ 586	2
+ 144	9
+ 112	2
+ 259	7
+ 949	3
+ 183	9
+ 570	2
+ 904	2
+ 331	5
+ 3	10
+ 385	3
+ 726	8
+ 20	2
+ 549	2
+ 56	2
+ 351	6
+ 330	5
+ 525	4
+ 658	8
+ 144	6
+ 45	3
+ 458	6
+ 513	4
+ 830	8
+ 911	8
+ 841	3
+ 112	6
+ 94	1
+ 810	6
+ 305	9
+ 806	7
+ 508	1
+ 150	0
+ 577	8
+ 817	7
+ 416	9
+ 49	9
+ 477	6
+ 236	3
+ 405	1
+ 140	2
+ 443	3
+ 812	5
+ 385	6
+ 181	7
+ 489	10
+ 345	10
+ 122	5
+ 30	7
+ 304	8
+ 421	7
+ 710	5
+ 594	2
+ 31	8
+ 493	4
+ 977	6
+ 681	4
+ 886	5
+ 958	3
+ 116	1
+ 960	6
+ 126	3
+ 602	2
+ 801	6
+ 948	1
+ 480	4
+ 825	2
+ 840	4
+ 376	9
+ 249	9
+ 307	2
+ 504	10
+ 646	4
+ 482	6
+ 661	6
+ 744	6
+ 203	9
+ 927	8
+ 118	7
+ 438	1
+ 833	9
+ 436	7
+ 108	3
+ 77	5
+ 147	3
+ 354	5
+ 552	9
+ 443	2
+ 248	9
+ 802	9
+ 523	5
+ 530	7
+ 416	5
+ 532	5
+ 186	10
+ 600	0
+ 887	0
+ 677	10
+ 312	8
+ 479	5
+ 80	8
+ 913	6
+ 691	4
+ 830	9
+ 281	6
+ 848	8
+ 178	4
+ 530	6
+ 835	1
+ 128	0
+ 31	7
+ 39	9
+ 765	7
+ 914	1
+ 470	4
+ 537	6
+ 226	4
+ 183	9
+ 806	0
+ 855	1
+ 645	7
+ 890	8
+ 81	4
+ 418	9
+ 482	5
+ 937	5
+ 274	10
+ 432	0
+ 692	3
+ 116	2
+ 738	7
+ 713	10
+ 102	9
+ 881	9
+ 909	7
+ 994	6
+ 439	9
+ 378	5
+ 304	8
+ 436	8
+ 341	4
+ 299	6
+ 349	7
+ 653	0
+ 76	8
+ 203	8
+ 421	9
+ 778	5
+ 812	7
+ 431	7
+ 395	4
+ 275	8
+ 309	7
+ 354	6
+ 449	8
+ 398	8
+ 163	7
+ 405	5
+ 428	1
+ 552	5
+ 828	8
+ 319	2
+ 672	1
+ 772	5
+ 756	2
+ 205	2
+ 628	5
+ 986	9
+ 134	3
+ 550	6
+ 130	9
+ 373	3
+ 644	8
+ 805	1
+ 837	4
+ 576	7
+ 113	9
+ 913	8
+ 992	7
+ 270	7
+ 889	5
+ 899	5
+ 956	9
+ 455	1
+ 225	0
+ 673	4
+ 952	0
+ 648	6
+ 825	5
+ 669	7
+ 811	2
+ 326	9
+ 140	2
+ 710	1
+ 925	10
+ 881	8
+ 454	8
+ 331	4
+ 665	8
+ 500	9
+ 791	2
+ 245	7
+ 219	9
+ 339	0
+ 347	0
+ 705	2
+ 253	0
+ 82	4
+ 270	8
+ 526	2
+ 771	4
+ 8	2
+ 186	3
+ 635	9
+ 126	1
+ 741	9
+ 307	10
+ 659	5
+ 878	10
+ 570	2
+ 5	3
+ 383	3
+ 306	5
+ 651	6
+ 256	2
+ 769	0
+ 583	8
+ 251	8
+ 117	9
+ 620	2
+ 21	4
+ 158	3
+ 346	8
+ 854	2
+ 814	4
+ 449	8
+ 699	8
+ 78	0
+ 296	7
+ 580	6
+ 905	3
+ 578	5
+ 127	8
+ 257	2
+ 715	9
+ 486	7
+ 237	6
+ 64	6
+ 461	9
+ 808	3
+ 342	3
+ 95	0
+ 89	2
+ 47	4
+ 901	6
+ 937	8
+ 977	5
+ 294	1
+ 344	6
+ 348	1
+ 428	8
+ 795	7
+ 478	9
+ 249	9
+ 777	1
+ 215	1
+ 314	3
+ 161	4
+ 482	2
+ 787	4
+ 835	7
+ 190	8
+ 238	5
+ 917	6
+ 36	3
+ 641	5
+ 100	4
+ 130	6
+ 295	4
+ 517	1
+ 436	7
+ 191	7
+ 42	4
+ 152	5
+ 559	9
+ 908	4
+ 663	1
+ 207	9
+ 583	1
+ 483	6
+ 390	1
+ 84	5
+ 561	2
+ 67	9
+ 593	6
+ 928	0
+ 316	1
+ 780	4
+ 470	9
+ 882	0
+ 871	8
+ 424	5
+ 888	6
+ 434	5
+ 756	9
+ 90	1
+ 42	2
+ 636	6
+ 387	7
+ 459	10
+ 288	4
+ 11	6
+ 505	8
+ 962	10
+ 722	8
+ 4	6
+ 634	4
+ 125	5
+ 59	6
+ 994	8
+ 476	1
+ 962	5
+ 257	6
+ 121	6
+ 301	6
+ 625	6
+ 966	6
+ 193	5
+ 426	2
+ 445	1
+ 1000	4
+ 739	6
+ 876	9
+ 157	9
+ 424	2
+ 751	9
+ 234	7
+ 418	5
+ 310	5
+ 135	6
+ 118	8
+ 200	1
+ 396	4
+ 555	8
+ 548	0
+ 969	5
+ 449	7
+ 183	3
+ 572	3
+ 261	10
+ 490	0
+ 896	7
+ 724	3
+ 214	0
+ 853	3
+ 645	10
+ 109	8
+ 56	5
+ 237	6
+ 326	8
+ 611	3
+ 334	1
+ 3	5
+ 385	6
+ 856	6
+ 571	3
+ 658	5
+ 69	4
+ 782	3
+ 415	6
+ 633	1
+ 607	7
+ 904	7
+ 248	1
+ 274	6
+ 927	9
+ 869	3
+ 945	9
+ 777	3
+ 447	6
+ 977	0
+ 978	6
+ 485	0
+ 16	3
+ 331	4
+ 902	10
+ 491	5
+ 707	4
+ 172	10
+ 537	4
+ 528	5
+ 331	4
+ 724	3
+ 268	5
+ 607	7
+ 134	6
+ 733	1
+ 219	2
+ 159	2
+ 485	5
+ 666	4
+ 455	2
+ 897	2
+ 552	1
+ 116	1
+ 515	6
+ 552	8
+ 42	3
+ 123	3
+ 777	7
+ 25	9
+ 314	8
+ 23	5
+ 975	2
+ 767	5
+ 673	4
+ 849	1
+ 591	7
+ 290	1
+ 815	4
+ 232	3
+ 51	8
+ 176	1
+ 61	3
+ 403	8
+ 28	4
+ 748	3
+ 185	8
+ 875	2
+ 953	6
+ 621	6
+ 76	5
+ 754	7
+ 216	0
+ 810	0
+ 451	0
+ 360	5
+ 826	5
+ 596	9
+ 834	10
+ 724	9
+ 426	5
+ 205	6
+ 244	1
+ 771	2
+ 724	4
+ 823	8
+ 863	6
+ 466	1
+ 622	3
+ 109	1
+ 318	5
+ 576	1
+ 6	2
+ 30	8
+ 170	8
+ 702	6
+ 226	9
+ 207	5
+ 989	10
+ 667	7
+ 372	5
+ 512	2
+ 67	10
+ 313	7
+ 254	4
+ 762	6
+ 892	3
+ 715	9
+ 510	7
+ 738	7
+ 498	4
+ 276	7
+ 348	5
+ 194	3
+ 462	9
+ 49	8
+ 350	6
+ 68	4
+ 539	4
+ 106	8
+ 804	9
+ 365	7
+ 207	1
+ 595	7
+ 824	3
+ 397	3
+ 773	7
+ 47	1
+ 156	2
+ 457	6
+ 101	5
+ 452	5
+ 66	5
+ 869	6
+ 902	10
+ 397	7
+ 844	8
+ 403	1
+ 841	10
+ 768	7
+ 330	2
+ 988	1
+ 837	0
+ 223	10
+ 276	7
+ 611	4
+ 185	1
+ 829	3
+ 583	7
+ 855	5
+ 672	3
+ 190	5
+ 14	6
+ 567	9
+ 590	3
+ 521	9
+ 498	5
+ 22	3
+ 544	2
+ 328	8
+ 925	9
+ 197	1
+ 1	0
+ 361	6
+ 723	2
+ 68	4
+ 469	3
+ 911	5
+ 851	5
+ 338	4
+ 812	9
+ 361	3
+ 368	4
+ 645	9
+ 629	10
+ 732	6
+ 911	9
+ 663	9
+ 955	0
+ 495	7
+ 241	6
+ 74	7
+ 820	10
+ 192	7
+ 462	5
+ 112	3
+ 388	5
+ 584	8
+ 856	2
+ 667	5
+ 201	4
+ 38	1
+ 329	7
+ 24	3
+ 726	5
+ 963	10
+ 81	0
+ 676	9
+ 21	9
+ 573	5
+ 398	7
+ 757	8
+ 157	3
+ 542	0
+ 569	2
+ 498	8
+ 608	5
+ 882	9
+ 238	9
+ 221	10
+ 424	2
+ 931	5
+ 221	6
+ 407	2
+ 476	10
+ 725	9
+ 664	5
+ 660	8
+ 822	2
+ 835	4
+ 411	3
+ 160	0
+ 870	0
+ 956	1
+ 947	2
+ 73	4
+ 362	0
+ 877	6
+ 612	3
+ 824	1
+ 265	5
+ 963	9
+ 31	6
+ 751	9
+ 825	6
+ 243	2
+ 920	4
+ 256	8
+ 445	2
+ 898	4
+ 390	10
+ 764	8
+ 975	6
+ 335	6
+ 926	2
+ 675	2
+ 708	6
+ 121	7
+ 261	9
+ 592	1
+ 458	8
+ 323	4
+ 238	6
+ 167	7
+ 791	1
+ 75	2
+ 36	8
+ 933	0
+ 481	3
+ 597	4
+ 427	3
+ 598	1
+ 910	7
+ 874	2
+ 590	5
+ 258	0
+ 300	6
+ 425	5
+ 160	6
+ 221	10
+ 657	3
+ 131	7
+ 134	1
+ 703	6
+ 332	3
+ 22	8
+ 573	6
+ 894	5
+ 339	8
+ 655	9
+ 234	9
+ 978	5
+ 494	4
+ 73	7
+ 995	3
+ 603	7
+ 588	7
+ 345	7
+ 799	0
+ 338	1
+ 349	4
+ 889	9
+ 980	8
+ 404	3
+ 551	1
+ 249	8
+ 972	2
+ 319	5
+ 629	4
+ 118	6
+ 685	7
+ 277	3
+ 456	6
+ 996	3
+ 670	3
+ 385	0
+ 694	3
+ 940	7
+ 57	3
+ 993	6
+ 404	2
+ 392	4
+ 468	7
+ 840	1
+ 103	10
+ 721	8
+ 680	10
+ 61	1
+ 620	1
+ 392	3
+ 391	8
+ 310	1
+ 51	3
+ 759	1
+ 595	8
+ 716	10
+ 993	1
+ 374	5
+ 819	2
+ 558	9
+ 172	3
+ 710	9
+ 278	8
+ 989	9
+ 829	4
+ 188	2
+ 158	5
+ 305	2
+ 748	1
+ 317	3
+ 815	0
+ 341	8
+ 141	7
+ 270	10
+ 929	8
+ 883	1
+ 108	6
+ 954	4
+ 364	9
+ 283	2
+ 324	5
+ 413	5
+ 970	7
+ 691	7
+ 781	0
+ 61	6
+ 41	4
+ 405	2
+ 118	7
+ 142	0
+ 504	0
+ 148	6
+ 618	1
+ 997	10
+ 46	3
+ 175	4
+ 752	6
+ 852	7
+ 306	5
+ 440	1
+ 552	5
+ 684	6
+ 904	1
+ 775	0
+ 764	9
+ 68	3
+ 942	2
+ 880	6
+ 319	9
+ 542	4
+ 157	7
+ 734	9
+ 306	6
+ 632	6
+ 130	1
+ 699	7
+ 574	4
+ 275	5
+ 472	1
+ 500	2
+ 968	6
+ 505	9
+ 785	4
+ 470	1
+ 261	0
+ 468	4
+ 730	2
+ 328	0
+ 788	10
+ 648	9
+ 32	3
+ 601	6
+ 730	9
+ 83	2
+ 926	6
+ 439	9
+ 151	9
+ 803	9
+ 327	3
+ 39	6
+ 285	5
+ 7	0
+ 709	3
+ 52	5
+ 294	7
+ 416	3
+ 47	0
+ 931	8
+ 892	0
+ 979	8
+ 597	4
+ 712	7
+ 361	5
+ 685	7
+ 789	7
+ 277	1
+ 231	3
+ 89	9
+ 618	1
+ 437	9
+ 840	9
+ 237	9
+ 869	2
+ 664	8
+ 181	6
+ 580	8
+ 61	3
+ 527	4
+ 808	2
+ 110	6
+ 936	4
+ 671	2
+ 670	8
+ 106	3
+ 901	5
+ 199	7
+ 395	4
+ 629	3
+ 603	3
+ 26	8
+ 936	6
+ 562	10
+ 898	1
+ 418	7
+ 301	5
+ 303	2
+ 915	10
+ 403	6
+ 733	5
+ 873	6
+ 52	1
+ 376	4
+ 508	0
+ 712	1
+ 297	7
+ 894	2
+ 344	5
+ 229	2
+ 546	6
+ 948	8
+ 176	3
+ 84	1
+ 225	5
+ 677	10
+ 996	5
+ 592	0
+ 622	10
+ 495	1
+ 972	2
+ 240	3
+ 944	1
+ 502	3
+ 591	7
+ 530	1
+ 379	5
+ 984	6
+ 730	1
+ 646	10
+ 555	3
+ 912	6
+ 873	5
+ 598	5
+ 472	1
+ 625	4
+ 299	9
+ 713	2
+ 1000	2
+ 531	6
+ 946	1
+ 728	3
+ 540	7
+ 881	3
+ 781	5
+ 223	3
+ 850	1
+ 885	7
+ 639	5
+ 218	1
+ 576	8
+ 555	9
+ 707	3
+ 120	7
+ 483	7
+ 298	4
+ 712	0
+ 755	3
+ 739	6
+ 521	5
+ 162	7
+ 854	0
+ 880	7
+ 735	5
+ 223	10
+ 630	8
+ 795	2
+ 676	5
+ 454	8
+ 208	9
+ 446	5
+ 367	2
+ 532	1
+ 410	3
+ 757	9
+ 789	9
+ 676	6
+ 931	6
+ 384	7
+ 74	6
+ 618	7
+ 407	4
+ 890	1
+ 915	3
+ 878	1
+ 281	3
+ 629	6
+ 482	2
+ 769	9
+ 431	5
+ 824	2
+ 445	5
+ 865	4
+ 55	2
+ 42	1
+ 855	7
+ 834	3
+ 73	7
+ 344	10
+ 68	2
+ 111	3
+ 545	7
+ 996	0
+ 901	8
+ 920	3
+ 291	7
+ 553	7
+ 243	4
+ 111	3
+ 666	2
+ 427	5
+ 812	3
+ 783	9
+ 985	1
+ 873	1
+ 348	10
+ 401	9
+ 724	4
+ 921	6
+ 163	8
+ 957	5
+ 584	5
+ 189	8
+ 928	3
+ 125	6
+ 452	6
+ 114	3
+ 814	9
+ 150	8
+ 23	0
+ 851	4
+ 7	3
+ 265	7
+ 650	2
+ 356	8
+ 25	3
+ 266	6
+ 824	5
+ 436	8
+ 754	6
+ 345	2
+ 114	5
+ 471	9
+ 356	6
+ 726	4
+ 644	6
+ 751	7
+ 829	0
+ 383	5
+ 201	7
+ 291	2
+ 53	6
+ 836	9
+ 11	3
+ 628	8
+ 834	10
+ 972	9
+ 433	4
+ 875	8
+ 63	6
+ 170	7
+ 178	9
+ 359	0
+ 937	7
+ 487	1
+ 481	8
+ 365	5
+ 335	2
+ 411	3
+ 472	0
+ 112	3
+ 13	1
+ 254	4
+ 526	1
+ 236	6
+ 730	4
+ 297	9
+ 327	7
+ 916	3
+ 399	4
+ 402	9
+ 181	8
+ 414	5
+ 967	8
+ 862	4
+ 865	10
+ 745	9
+ 58	10
+ 325	6
+ 128	6
+ 173	9
+ 967	5
+ 767	3
+ 127	7
+ 558	5
+ 86	10
+ 405	3
+ 726	8
+ 783	7
+ 645	6
+ 132	5
+ 619	9
+ 388	7
+ 876	7
+ 260	0
+ 273	4
+ 862	2
+ 903	6
+ 534	0
+ 312	1
+ 555	4
+ 51	10
+ 665	8
+ 780	4
+ 469	4
+ 93	6
+ 934	7
+ 477	3
+ 388	4
+ 34	6
+ 356	3
+ 81	2
+ 546	10
+ 847	1
+ 15	2
+ 171	6
+ 558	2
+ 531	2
+ 998	3
+ 672	5
+ 735	8
+ 67	7
+ 476	5
+ 991	9
+ 897	0
+ 512	3
+ 332	6
+ 471	9
+ 578	3
+ 958	6
+ 477	1
+ 163	0
+ 351	7
+ 259	3
+ 4	9
+ 816	7
+ 695	9
+ 409	2
+ 427	4
+ 35	3
+ 426	5
+ 576	8
+ 140	0
+ 636	7
+ 365	6
+ 311	8
+ 724	5
+ 877	1
+ 167	1
+ 424	2
+ 67	2
+ 911	8
+ 122	3
+ 932	5
+ 721	10
+ 872	1
+ 514	4
+ 905	7
+ 495	5
+ 372	9
+ 135	7
+ 702	9
+ 156	6
+ 933	3
+ 715	4
+ 495	8
+ 596	4
+ 543	7
+ 726	5
+ 267	4
+ 442	1
+ 595	10
+ 588	5
+ 610	1
+ 40	10
+ 943	2
+ 665	6
+ 34	8
+ 224	10
+ 145	9
+ 324	6
+ 721	9
+ 45	3
+ 638	8
+ 739	9
+ 219	2
+ 45	8
+ 138	6
+ 314	7
+ 717	4
+ 730	7
+ 529	4
+ 305	6
+ 216	5
+ 531	4
+ 468	9
+ 0	2
+ 776	0
+ 453	4
+ 817	2
+ 320	0
+ 373	4
+ 850	5
+ 998	2
+ 259	7
+ 518	10
+ 375	0
+ 384	7
+ 611	6
+ 209	1
+ 961	7
+ 998	10
+ 866	8
+ 7	3
+ 188	8
+ 511	5
+ 861	9
+ 873	7
+ 395	9
+ 875	7
+ 586	4
+ 643	10
+ 441	0
+ 642	1
+ 628	9
+ 195	6
+ 529	2
+ 551	4
+ 967	6
+ 714	2
+ 383	2
+ 663	2
+ 109	5
+ 955	5
+ 408	8
+ 158	10
+ 223	8
+ 956	7
+ 829	6
+ 717	5
+ 449	9
+ 46	10
+ 104	6
+ 373	1
+ 156	1
+ 226	5
+ 313	9
+ 781	4
+ 426	7
+ 926	8
+ 566	1
+ 830	8
+ 886	8
+ 454	7
+ 384	2
+ 172	8
+ 82	2
+ 811	2
+ 816	2
+ 257	10
+ 272	5
+ 509	6
+ 373	3
+ 6	8
+ 27	9
+ 635	6
+ 16	5
+ 382	9
+ 250	8
+ 617	6
+ 7	8
+ 468	1
+ 7	3
+ 275	8
+ 464	5
+ 794	7
+ 16	3
+ 320	4
+ 593	3
+ 189	6
+ 259	8
+ 213	3
+ 288	6
+ 177	5
+ 431	8
+ 173	4
+ 583	6
+ 527	6
+ 920	8
+ 413	4
+ 335	2
+ 120	4
+ 509	4
+ 740	1
+ 767	9
+ 722	0
+ 754	9
+ 301	0
+ 530	5
+ 581	10
+ 273	8
+ 400	9
+ 395	9
+ 446	3
+ 728	9
+ 700	1
+ 65	8
+ 414	6
+ 260	2
+ 676	0
+ 84	4
+ 52	8
+ 334	4
+ 880	9
+ 832	5
+ 826	1
+ 216	2
+ 960	6
+ 152	4
+ 927	9
+ 265	6
+ 943	3
+ 446	4
+ 904	7
+ 511	6
+ 733	6
+ 979	8
+ 433	3
+ 138	3
+ 177	10
+ 775	0
+ 74	10
+ 227	0
+ 603	4
+ 441	5
+ 259	7
+ 157	2
+ 37	6
+ 559	9
+ 309	1
+ 522	0
+ 666	5
+ 827	1
+ 814	10
+ 413	10
+ 935	2
+ 993	0
+ 180	2
+ 44	8
+ 599	5
+ 312	9
+ 191	5
+ 61	2
+ 73	6
+ 169	4
+ 691	7
+ 424	4
+ 192	3
+ 456	0
+ 217	9
+ 997	2
+ 57	10
+ 162	2
+ 210	2
+ 19	8
+ 690	3
+ 668	9
+ 801	7
+ 109	9
+ 350	3
+ 256	0
+ 969	7
+ 399	2
+ 932	9
+ 168	1
+ 724	2
+ 301	8
+ 154	5
+ 19	4
+ 668	0
+ 173	4
+ 370	8
+ 239	2
+ 570	3
+ 45	9
+ 626	3
+ 962	6
+ 982	4
+ 757	9
+ 216	9
+ 63	9
+ 89	4
+ 722	2
+ 828	7
+ 606	5
+ 779	8
+ 854	1
+ 619	1
+ 320	2
+ 441	4
+ 110	1
+ 666	1
+ 663	6
+ 432	4
+ 562	6
+ 344	6
+ 588	4
+ 989	3
+ 676	8
+ 51	3
+ 313	8
+ 61	2
+ 978	7
+ 260	3
+ 870	7
+ 663	10
+ 768	3
+ 50	4
+ 978	5
+ 850	5
+ 129	2
+ 165	7
+ 628	2
+ 27	3
+ 971	1
+ 586	3
+ 907	6
+ 450	9
+ 327	7
+ 184	2
+ 410	8
+ 175	2
+ 177	2
+ 608	2
+ 707	5
+ 694	8
+ 652	9
+ 554	3
+ 13	6
+ 584	10
+ 658	2
+ 267	6
+ 816	7
+ 450	1
+ 428	6
+ 339	8
+ 480	5
+ 16	7
+ 739	6
+ 811	4
+ 82	5
+ 283	7
+ 364	8
+ 15	4
+ 417	6
+ 360	1
+ 769	6
+ 640	6
+ 345	1
+ 728	8
+ 723	1
+ 611	2
+ 581	6
+ 861	3
+ 252	7
+ 767	3
+ 177	1
+ 69	5
+ 887	1
+ 918	3
+ 684	3
+ 380	5
+ 906	0
+ 38	3
+ 110	8
+ 24	8
+ 833	6
+ 37	4
+ 263	9
+ 733	5
+ 570	5
+ 849	7
+ 550	9
+ 288	4
+ 2	2
+ 742	7
+ 484	1
+ 139	4
+ 142	2
+ 640	3
+ 942	7
+ 85	8
+ 300	1
+ 188	6
+ 20	9
+ 76	6
+ 422	9
+ 336	10
+ 843	6
+ 409	8
+ 830	2
+ 531	3
+ 274	7
+ 704	4
+ 846	3
+ 667	8
+ 8	8
+ 564	3
+ 874	8
+ 870	9
+ 674	9
+ 483	1
+ 871	8
+ 68	7
+ 444	5
+ 559	3
+ 629	1
+ 588	9
+ 760	3
+ 318	6
+ 636	10
+ 395	6
+ 737	10
+ 951	6
+ 711	8
+ 505	4
+ 767	10
+ 480	6
+ 807	5
+ 353	3
+ 25	9
+ 525	7
+ 2	1
+ 555	8
+ 405	9
+ 369	0
+ 858	8
+ 684	6
+ 723	6
+ 207	4
+ 456	7
+ 818	2
+ 701	3
+ 862	5
+ 845	2
+ 759	9
+ 127	3
+ 523	1
+ 397	1
+ 892	8
+ 952	3
+ 841	8
+ 25	5
+ 406	7
+ 160	6
+ 181	6
+ 325	10
+ 840	0
+ 297	7
+ 534	1
+ 916	3
+ 13	0
+ 577	5
+ 172	10
+ 615	1
+ 775	6
+ 325	6
+ 377	3
+ 141	8
+ 97	3
+ 396	3
+ 918	7
+ 278	8
+ 747	6
+ 459	3
+ 717	4
+ 574	7
+ 418	2
+ 265	6
+ 125	9
+ 655	9
+ 447	10
+ 516	8
+ 329	7
+ 606	4
+ 959	0
+ 705	9
+ 723	10
+ 634	5
+ 557	1
+ 751	3
+ 468	3
+ 4	9
+ 477	3
+ 477	6
+ 149	1
+ 501	6
+ 111	0
+ 419	4
+ 674	0
+ 867	6
+ 28	6
+ 509	8
+ 554	1
+ 221	1
+ 236	10
+ 385	7
+ 298	4
+ 590	8
+ 657	1
+ 375	8
+ 200	9
+ 402	3
+ 893	8
+ 752	6
+ 847	6
+ 199	9
+ 190	7
+ 626	7
+ 852	8
+ 854	1
+ 819	2
+ 792	1
+ 627	4
+ 891	3
+ 450	3
+ 91	6
+ 142	5
+ 961	0
+ 315	7
+ 602	2
+ 331	8
+ 37	5
+ 510	7
+ 265	4
+ 509	1
+ 450	3
+ 358	2
+ 445	10
+ 624	3
+ 270	1
+ 602	4
+ 724	7
+ 854	7
+ 779	2
+ 397	4
+ 331	7
+ 182	4
+ 248	7
+ 31	5
+ 55	5
+ 632	5
+ 869	10
+ 746	3
+ 976	4
+ 649	2
+ 445	3
+ 606	2
+ 995	5
+ 853	8
+ 629	2
+ 155	10
+ 977	3
+ 329	2
+ 30	4
+ 738	1
+ 901	4
+ 589	8
+ 361	3
+ 84	3
+ 706	7
+ 582	2
+ 984	2
+ 319	10
+ 648	2
+ 753	3
+ 421	9
+ 237	4
+ 246	6
+ 623	3
+ 926	4
+ 361	8
+ 732	9
+ 597	1
+ 285	7
+ 430	10
+ 414	0
+ 141	4
+ 201	5
+ 378	8
+ 631	1
+ 126	1
+ 40	4
+ 449	3
+ 929	1
+ 562	9
+ 433	9
+ 682	2
+ 872	3
+ 258	2
+ 960	7
+ 147	4
+ 700	3
+ 773	9
+ 747	2
+ 749	4
+ 282	9
+ 429	3
+ 239	9
+ 608	2
+ 949	2
+ 23	4
+ 92	7
+ 546	10
+ 984	8
+ 121	9
+ 491	3
+ 318	2
+ 556	1
+ 91	3
+ 242	8
+ 680	5
+ 716	1
+ 846	10
+ 986	5
+ 123	9
+ 624	1
+ 317	7
+ 851	9
+ 681	8
+ 668	8
+ 778	2
+ 71	1
+ 350	6
+ 186	4
+ 929	4
+ 282	6
+ 952	10
+ 718	8
+ 952	7
+ 252	1
+ 639	9
+ 221	10
+ 593	1
+ 820	3
+ 906	5
+ 76	7
+ 647	1
+ 779	10
+ 774	10
+ 438	7
+ 393	7
+ 312	3
+ 718	0
+ 143	7
+ 734	4
+ 745	4
+ 271	10
+ 330	9
+ 37	1
+ 138	9
+ 637	2
+ 627	3
+ 361	4
+ 281	1
+ 372	7
+ 838	8
+ 440	1
+ 110	2
+ 180	3
+ 828	9
+ 647	6
+ 287	9
+ 538	6
+ 782	6
+ 766	9
+ 518	4
+ 133	1
+ 688	5
+ 551	10
+ 629	9
+ 689	5
+ 688	1
+ 616	8
+ 287	8
+ 50	1
+ 709	7
+ 687	10
+ 616	2
+ 613	4
+ 801	4
+ 316	3
+ 782	4
+ 464	5
+ 944	0
+ 439	6
+ 939	1
+ 39	6
+ 257	7
+ 425	5
+ 451	5
+ 658	2
+ 173	3
+ 157	8
+ 570	8
+ 186	4
+ 148	5
+ 690	9
+ 951	2
+ 400	9
+ 170	8
+ 468	1
+ 967	5
+ 735	2
+ 162	2
+ 768	6
+ 635	4
+ 774	8
+ 771	9
+ 596	3
+ 700	8
+ 712	8
+ 283	4
+ 778	2
+ 556	2
+ 129	7
+ 17	6
+ 834	10
+ 104	6
+ 208	3
+ 729	10
+ 879	4
+ 403	7
+ 171	2
+ 583	8
+ 516	3
+ 548	2
+ 131	8
+ 631	9
+ 66	2
+ 86	2
+ 912	1
+ 792	7
+ 86	9
+ 315	3
+ 161	0
+ 272	0
+ 408	7
+ 693	6
+ 850	3
+ 346	4
+ 559	9
+ 595	7
+ 726	2
+ 598	8
+ 412	7
+ 987	3
+ 786	8
+ 70	9
+ 676	4
+ 167	8
+ 430	4
+ 877	8
+ 113	6
+ 417	10
+ 846	8
+ 330	4
+ 657	9
+ 94	4
+ 150	7
+ 175	6
+ 376	2
+ 886	2
+ 942	10
+ 34	6
+ 342	2
+ 455	8
+ 639	3
+ 610	8
+ 902	0
+ 715	7
+ 789	0
+ 152	4
+ 969	2
+ 829	1
+ 938	0
+ 682	3
+ 166	6
+ 475	1
+ 525	5
+ 725	9
+ 709	2
+ 639	3
+ 511	2
+ 99	4
+ 275	8
+ 160	1
+ 859	3
+ 509	8
+ 558	3
+ 948	5
+ 341	6
+ 809	5
+ 198	3
+ 614	7
+ 792	3
+ 590	5
+ 519	2
+ 848	0
+ 478	9
+ 443	8
+ 761	6
+ 816	6
+ 916	3
+ 448	5
+ 663	4
+ 970	0
+ 26	8
+ 512	2
+ 62	1
+ 947	9
+ 465	5
+ 354	10
+ 766	2
+ 14	2
+ 149	5
+ 996	9
+ 61	8
+ 530	10
+ 138	10
+ 451	8
+ 374	4
+ 806	4
+ 199	3
+ 623	3
+ 443	6
+ 115	9
+ 107	5
+ 893	9
+ 671	9
+ 117	8
+ 365	1
+ 730	4
+ 926	3
+ 403	1
+ 237	9
+ 865	6
+ 275	7
+ 10	5
+ 988	6
+ 736	4
+ 204	9
+ 340	3
+ 321	2
+ 185	10
+ 140	3
+ 812	5
+ 416	5
+ 931	3
+ 802	3
+ 405	0
+ 189	3
+ 650	5
+ 941	7
+ 939	9
+ 295	7
+ 361	5
+ 526	7
+ 810	8
+ 934	10
+ 839	1
+ 297	7
+ 579	7
+ 194	5
+ 54	10
+ 845	5
+ 36	0
+ 730	7
+ 498	7
+ 346	4
+ 602	6
+ 112	10
+ 140	6
+ 665	9
+ 486	6
+ 945	3
+ 673	2
+ 977	3
+ 954	2
+ 763	0
+ 167	6
+ 468	2
+ 641	2
+ 888	1
+ 870	2
+ 576	5
+ 876	7
+ 434	0
+ 326	1
+ 965	8
+ 698	9
+ 136	4
+ 151	1
+ 624	1
+ 284	4
+ 114	5
+ 994	6
+ 654	6
+ 780	5
+ 773	7
+ 777	3
+ 122	7
+ 36	6
+ 669	4
+ 655	6
+ 174	4
+ 544	3
+ 724	7
+ 423	3
+ 801	7
+ 734	9
+ 158	7
+ 497	8
+ 362	3
+ 354	1
+ 928	1
+ 484	0
+ 784	5
+ 605	5
+ 882	3
+ 87	1
+ 613	6
+ 365	3
+ 326	8
+ 685	1
+ 495	4
+ 42	7
+ 148	5
+ 465	5
+ 816	8
+ 646	7
+ 950	1
+ 793	7
+ 649	4
+ 187	5
+ 658	3
+ 587	3
+ 904	10
+ 608	2
+ 740	3
+ 356	2
+ 712	4
+ 888	9
+ 937	4
+ 370	8
+ 172	0
+ 497	1
+ 146	3
+ 855	8
+ 687	0
+ 327	3
+ 315	9
+ 616	2
+ 866	2
+ 449	6
+ 516	8
+ 842	2
+ 203	7
+ 89	1
+ 83	5
+ 893	3
+ 475	4
+ 376	6
+ 679	2
+ 416	4
+ 272	7
+ 711	6
+ 656	3
+ 806	5
+ 550	3
+ 129	1
+ 60	10
+ 295	3
+ 701	4
+ 403	8
+ 843	3
+ 39	3
+ 686	4
+ 940	4
+ 645	4
+ 732	9
+ 99	4
+ 504	8
+ 770	3
+ 278	3
+ 565	4
+ 386	6
+ 377	7
+ 887	1
+ 65	3
+ 861	9
+ 586	9
+ 227	3
+ 315	2
+ 638	10
+ 523	4
+ 878	6
+ 812	4
+ 378	6
+ 693	7
+ 901	3
+ 62	3
+ 881	4
+ 968	8
+ 517	0
+ 58	4
+ 941	6
+ 278	2
+ 917	6
+ 335	6
+ 553	9
+ 923	4
+ 480	7
+ 813	9
+ 316	5
+ 514	2
+ 763	6
+ 504	6
+ 16	5
+ 413	5
+ 505	5
+ 911	4
+ 116	2
+ 614	0
+ 782	9
+ 587	3
+ 806	5
+ 767	3
+ 246	6
+ 145	6
+ 86	7
+ 780	8
+ 236	3
+ 494	3
+ 756	9
+ 785	3
+ 378	7
+ 707	5
+ 885	3
+ 527	7
+ 269	1
+ 4	1
+ 625	8
+ 362	9
+ 351	5
+ 433	4
+ 166	2
+ 287	4
+ 497	8
+ 655	3
+ 688	4
+ 514	1
+ 137	2
+ 561	0
+ 541	1
+ 690	8
+ 202	7
+ 885	8
+ 464	2
+ 698	8
+ 753	1
+ 252	9
+ 345	5
+ 322	8
+ 321	10
+ 95	0
+ 418	6
+ 76	6
+ 829	6
+ 576	4
+ 725	3
+ 180	9
+ 959	1
+ 755	4
+ 311	5
+ 238	1
+ 585	5
+ 983	9
+ 30	3
+ 772	4
+ 283	9
+ 360	7
+ 476	4
+ 256	3
+ 73	8
+ 675	8
+ 98	9
+ 726	1
+ 919	5
+ 480	2
+ 934	7
+ 293	5
+ 208	3
+ 450	2
+ 582	2
+ 588	9
+ 88	9
+ 567	6
+ 384	8
+ 869	5
+ 655	5
+ 255	8
+ 398	10
+ 810	3
+ 461	3
+ 546	4
+ 8	8
+ 915	2
+ 115	4
+ 453	7
+ 586	0
+ 562	7
+ 988	1
+ 238	4
+ 952	1
+ 829	6
+ 650	1
+ 359	0
+ 64	2
+ 365	5
+ 459	9
+ 920	5
+ 749	8
+ 683	9
+ 200	1
+ 561	8
+ 176	1
+ 460	2
+ 252	7
+ 537	2
+ 805	4
+ 810	5
+ 449	2
+ 503	5
+ 338	9
+ 37	8
+ 778	10
+ 264	5
+ 793	9
+ 390	10
+ 83	10
+ 778	3
+ 74	2
+ 424	3
+ 936	10
+ 530	7
+ 326	3
+ 196	8
+ 509	7
+ 287	8
+ 567	3
+ 645	3
+ 282	9
+ 871	1
+ 856	3
+ 68	9
+ 212	8
+ 198	3
+ 84	6
+ 613	0
+ 583	1
+ 761	9
+ 484	10
+ 684	10
+ 657	10
+ 841	2
+ 296	5
+ 569	6
+ 395	4
+ 653	3
+ 702	7
+ 190	9
+ 567	4
+ 201	7
+ 11	8
+ 670	6
+ 957	4
+ 504	4
+ 389	2
+ 435	0
+ 160	3
+ 270	5
+ 761	8
+ 34	2
+ 279	7
+ 407	10
+ 408	6
+ 894	10
+ 986	1
+ 625	10
+ 908	3
+ 592	9
+ 727	1
+ 307	1
+ 285	7
+ 162	4
+ 17	4
+ 900	8
+ 270	9
+ 934	5
+ 622	3
+ 529	0
+ 939	4
+ 5	9
+ 518	6
+ 923	4
+ 925	5
+ 292	7
+ 612	6
+ 768	9
+ 341	9
+ 341	4
+ 362	2
+ 136	6
+ 175	1
+ 181	8
+ 411	7
+ 826	4
+ 134	8
+ 275	7
+ 461	2
+ 79	4
+ 714	4
+ 38	3
+ 970	8
+ 222	3
+ 737	6
+ 668	1
+ 803	8
+ 732	10
+ 873	9
+ 774	3
+ 623	6
+ 635	8
+ 431	9
+ 409	9
+ 108	5
+ 278	8
+ 858	3
+ 147	8
+ 123	4
+ 139	9
+ 931	8
+ 959	7
+ 611	7
+ 712	5
+ 604	5
+ 769	2
+ 86	4
+ 985	5
+ 313	4
+ 408	4
+ 881	7
+ 243	7
+ 2	4
+ 568	1
+ 760	7
+ 985	7
+ 514	9
+ 426	1
+ 636	1
+ 608	2
+ 624	4
+ 467	7
+ 780	5
+ 227	1
+ 846	6
+ 514	7
+ 321	8
+ 467	3
+ 148	0
+ 448	9
+ 742	4
+ 598	3
+ 378	0
+ 380	0
+ 162	10
+ 253	8
+ 365	7
+ 495	1
+ 173	7
+ 238	0
+ 356	8
+ 746	7
+ 510	2
+ 0	7
+ 248	4
+ 565	10
+ 882	2
+ 246	3
+ 187	6
+ 272	3
+ 614	5
+ 134	10
+ 246	6
+ 125	4
+ 350	4
+ 437	7
+ 115	2
+ 384	6
+ 396	4
+ 282	6
+ 833	8
+ 634	7
+ 10	9
+ 973	2
+ 507	2
+ 545	1
+ 771	7
+ 100	0
+ 307	2
+ 435	7
+ 588	9
+ 364	7
+ 55	7
+ 327	5
+ 132	6
+ 95	10
+ 455	7
+ 679	5
+ 609	7
+ 661	1
+ 897	2
+ 237	7
+ 885	3
+ 685	2
+ 563	1
+ 849	2
+ 991	2
+ 853	0
+ 961	2
+ 497	1
+ 788	6
+ 57	2
+ 320	7
+ 708	9
+ 387	4
+ 46	3
+ 575	3
+ 953	5
+ 620	6
+ 652	2
+ 758	5
+ 333	7
+ 714	2
+ 795	7
+ 365	3
+ 767	2
+ 883	8
+ 396	2
+ 559	1
+ 133	9
+ 472	2
+ 232	0
+ 461	2
+ 507	1
+ 823	2
+ 264	6
+ 659	6
+ 329	4
+ 783	1
+ 47	1
+ 416	8
+ 300	3
+ 638	7
+ 502	2
+ 800	6
+ 145	3
+ 814	4
+ 319	3
+ 561	8
+ 356	4
+ 984	6
+ 964	6
+ 218	3
+ 16	0
+ 418	1
+ 148	8
+ 878	4
+ 133	5
+ 144	6
+ 714	9
+ 270	9
+ 217	1
+ 235	5
+ 358	8
+ 362	7
+ 180	3
+ 335	1
+ 989	6
+ 437	0
+ 553	9
+ 69	7
+ 689	9
+ 149	8
+ 463	3
+ 457	2
+ 238	7
+ 36	5
+ 810	3
+ 990	2
+ 67	4
+ 883	2
+ 698	2
+ 390	7
+ 771	8
+ 693	3
+ 683	8
+ 26	4
+ 709	2
+ 194	2
+ 469	7
+ 349	7
+ 377	4
+ 161	2
+ 656	2
+ 355	7
+ 503	2
+ 969	2
+ 456	4
+ 889	2
+ 187	6
+ 553	9
+ 344	6
+ 241	1
+ 754	4
+ 225	2
+ 85	6
+ 929	5
+ 960	1
+ 650	6
+ 241	0
+ 339	7
+ 244	3
+ 946	7
+ 668	8
+ 928	9
+ 418	5
+ 725	8
+ 59	10
+ 815	8
+ 400	0
+ 35	5
+ 614	10
+ 948	6
+ 53	6
+ 190	3
+ 604	5
+ 39	8
+ 838	10
+ 547	5
+ 820	5
+ 361	2
+ 955	1
+ 0	0
+ 52	8
+ 826	5
+ 855	9
+ 938	5
+ 825	9
+ 43	9
+ 484	2
+ 173	1
+ 762	2
+ 935	6
+ 196	5
+ 107	0
+ 957	5
+ 254	9
+ 554	3
+ 926	6
+ 69	8
+ 58	9
+ 614	10
+ 393	4
+ 882	4
+ 317	4
+ 669	5
+ 454	4
+ 701	4
+ 32	9
+ 871	1
+ 913	8
+ 607	2
+ 740	2
+ 421	7
+ 767	5
+ 418	8
+ 414	0
+ 821	8
+ 470	7
+ 244	8
+ 69	9
+ 277	5
+ 345	10
+ 912	4
+ 875	8
+ 516	8
+ 611	1
+ 956	4
+ 285	4
+ 16	1
+ 867	4
+ 878	3
+ 466	7
+ 88	9
+ 401	3
+ 723	5
+ 245	0
+ 992	6
+ 978	9
+ 967	9
+ 687	5
+ 642	3
+ 607	6
+ 648	9
+ 974	7
+ 944	8
+ 98	8
+ 122	6
+ 520	2
+ 500	9
+ 541	2
+ 391	8
+ 223	4
+ 376	2
+ 288	3
+ 55	10
+ 827	7
+ 273	4
+ 294	9
+ 325	3
+ 585	3
+ 108	7
+ 92	2
+ 248	6
+ 440	7
+ 533	10
+ 971	9
+ 767	2
+ 308	1
+ 395	6
+ 487	4
+ 571	3
+ 146	8
+ 747	4
+ 765	1
+ 707	4
+ 342	8
+ 34	4
+ 46	3
+ 46	5
+ 30	6
+ 466	0
+ 504	2
+ 194	8
+ 378	6
+ 408	9
+ 38	10
+ 178	2
+ 823	9
+ 624	6
+ 997	3
+ 939	3
+ 147	10
+ 772	2
+ 255	8
+ 677	3
+ 397	1
+ 286	9
+ 378	5
+ 712	8
+ 69	1
+ 620	1
+ 98	8
+ 291	9
+ 722	9
+ 509	7
+ 245	4
+ 58	4
+ 421	8
+ 584	7
+ 648	3
+ 962	0
+ 405	2
+ 945	8
+ 727	7
+ 538	8
+ 776	2
+ 903	9
+ 956	2
+ 796	7
+ 108	3
+ 397	4
+ 753	5
+ 745	2
+ 285	3
+ 850	9
+ 591	8
+ 977	10
+ 59	9
+ 780	8
+ 578	3
+ 583	4
+ 476	5
+ 228	4
+ 679	0
+ 110	8
+ 329	5
+ 141	1
+ 963	9
+ 255	2
+ 215	1
+ 181	8
+ 917	2
+ 803	10
+ 80	6
+ 763	7
+ 900	3
+ 12	4
+ 831	2
+ 809	5
+ 264	9
+ 297	6
+ 427	4
+ 674	4
+ 324	9
+ 638	5
+ 34	8
+ 346	10
+ 978	1
+ 928	1
+ 730	7
+ 716	6
+ 36	7
+ 7	9
+ 969	8
+ 378	2
+ 735	7
+ 826	2
+ 113	5
+ 552	4
+ 429	2
+ 976	5
+ 10	3
+ 415	10
+ 470	3
+ 46	2
+ 33	8
+ 831	1
+ 490	8
+ 937	5
+ 654	3
+ 691	4
+ 990	5
+ 550	1
+ 17	1
+ 539	4
+ 292	5
+ 909	3
+ 837	3
+ 289	3
+ 666	3
+ 507	7
+ 96	3
+ 769	6
+ 176	7
+ 45	8
+ 21	7
+ 218	0
+ 253	8
+ 113	3
+ 870	7
+ 715	2
+ 167	6
+ 463	0
+ 947	8
+ 312	6
+ 87	8
+ 312	2
+ 157	1
+ 770	3
+ 787	8
+ 163	8
+ 551	4
+ 818	8
+ 150	9
+ 74	0
+ 583	8
+ 182	8
+ 414	6
+ 755	4
+ 397	1
+ 974	5
+ 886	3
+ 668	0
+ 368	4
+ 377	2
+ 253	5
+ 964	8
+ 921	8
+ 609	1
+ 713	7
+ 90	3
+ 472	3
+ 47	9
+ 917	8
+ 247	3
+ 869	2
+ 798	8
+ 508	5
+ 798	9
+ 905	2
+ 32	2
+ 714	10
+ 962	6
+ 777	6
+ 705	5
+ 253	8
+ 787	7
+ 67	8
+ 612	10
+ 636	9
+ 298	5
+ 80	1
+ 259	6
+ 563	1
+ 464	5
+ 232	5
+ 625	9
+ 491	6
+ 581	3
+ 157	3
+ 759	4
+ 82	5
+ 136	1
+ 380	7
+ 132	0
+ 607	4
+ 520	7
+ 526	8
+ 275	1
+ 837	7
+ 556	1
+ 235	2
+ 15	7
+ 768	6
+ 994	9
+ 882	8
+ 336	10
+ 299	5
+ 112	7
+ 220	2
+ 695	8
+ 675	2
+ 513	2
+ 995	8
+ 290	8
+ 527	8
+ 900	8
+ 27	9
+ 488	8
+ 510	5
+ 720	4
+ 235	1
+ 355	5
+ 528	5
+ 213	7
+ 711	9
+ 574	4
+ 122	1
+ 587	1
+ 876	9
+ 948	4
+ 723	8
+ 165	7
+ 764	7
+ 545	3
+ 134	3
+ 666	4
+ 321	0
+ 903	8
+ 489	1
+ 597	2
+ 23	2
+ 586	1
+ 259	2
+ 263	1
+ 50	2
+ 537	8
+ 60	7
+ 522	8
+ 354	1
+ 98	5
+ 331	8
+ 857	7
+ 786	8
+ 501	3
+ 876	1
+ 475	9
+ 269	1
+ 45	5
+ 234	3
+ 662	3
+ 518	2
+ 56	6
+ 900	6
+ 402	3
+ 644	5
+ 743	10
+ 264	6
+ 627	1
+ 360	1
+ 325	2
+ 226	8
+ 134	5
+ 861	2
+ 22	1
+ 486	7
+ 379	0
+ 882	4
+ 582	8
+ 12	10
+ 37	7
+ 484	8
+ 631	7
+ 379	3
+ 799	7
+ 387	1
+ 974	6
+ 925	1
+ 108	8
+ 287	1
+ 881	8
+ 813	3
+ 778	7
+ 695	4
+ 478	7
+ 344	5
+ 364	8
+ 294	10
+ 577	7
+ 254	4
+ 412	6
+ 500	4
+ 254	4
+ 495	4
+ 211	8
+ 491	1
+ 555	3
+ 352	3
+ 1000	0
+ 693	5
+ 755	0
+ 992	1
+ 865	3
+ 114	4
+ 959	4
+ 818	4
+ 9	3
+ 757	3
+ 743	3
+ 625	10
+ 34	1
+ 46	6
+ 421	4
+ 923	4
+ 445	6
+ 898	2
+ 653	9
+ 319	5
+ 175	4
+ 960	1
+ 802	8
+ 505	8
+ 96	3
+ 75	8
+ 515	7
+ 793	5
+ 817	8
+ 139	2
+ 236	1
+ 658	7
+ 677	6
+ 882	3
+ 445	2
+ 848	6
+ 635	8
+ 754	4
+ 586	3
+ 249	7
+ 523	3
+ 522	0
+ 24	3
+ 587	8
+ 153	7
+ 78	4
+ 787	7
+ 71	5
+ 291	10
+ 794	7
+ 155	6
+ 356	8
+ 451	1
+ 228	0
+ 370	5
+ 719	9
+ 801	2
+ 930	8
+ 556	5
+ 667	7
+ 242	7
+ 98	0
+ 481	2
+ 493	8
+ 123	3
+ 508	3
+ 929	9
+ 68	4
+ 974	3
+ 417	3
+ 772	1
+ 237	6
+ 378	2
+ 399	9
+ 683	1
+ 642	9
+ 811	7
+ 954	3
+ 910	4
+ 64	0
+ 734	6
+ 310	7
+ 437	4
+ 43	4
+ 674	5
+ 756	4
+ 596	10
+ 20	10
+ 158	4
+ 907	8
+ 485	5
+ 766	3
+ 290	7
+ 588	2
+ 167	7
+ 233	9
+ 224	5
+ 564	7
+ 922	6
+ 73	6
+ 67	8
+ 41	7
+ 820	1
+ 637	10
+ 480	5
+ 820	10
+ 94	6
+ 260	4
+ 306	8
+ 584	5
+ 500	8
+ 374	7
+ 361	9
+ 385	3
+ 545	5
+ 877	6
+ 286	9
+ 275	1
+ 979	9
+ 85	5
+ 457	9
+ 424	6
+ 492	7
+ 936	8
+ 531	5
+ 271	0
+ 337	6
+ 755	7
+ 583	1
+ 980	1
+ 599	9
+ 739	9
+ 776	0
+ 992	8
+ 926	1
+ 215	4
+ 982	6
+ 935	5
+ 322	9
+ 272	9
+ 391	5
+ 885	7
+ 189	6
+ 426	8
+ 780	4
+ 899	4
+ 264	6
+ 264	0
+ 652	3
+ 796	6
+ 332	0
+ 961	3
+ 649	9
+ 789	10
+ 767	1
+ 825	2
+ 605	7
+ 886	8
+ 349	3
+ 566	1
+ 719	5
+ 508	10
+ 103	8
+ 23	8
+ 28	8
+ 333	4
+ 830	3
+ 675	5
+ 190	5
+ 450	10
+ 525	3
+ 115	1
+ 984	0
+ 924	3
+ 313	5
+ 463	0
+ 955	10
+ 15	1
+ 743	0
+ 813	8
+ 858	1
+ 131	7
+ 440	8
+ 167	6
+ 270	6
+ 587	8
+ 892	7
+ 925	9
+ 702	8
+ 210	0
+ 339	7
+ 47	3
+ 643	1
+ 351	4
+ 101	2
+ 157	10
+ 310	3
+ 647	7
+ 93	8
+ 380	4
+ 432	10
+ 158	3
+ 668	1
+ 201	4
+ 933	4
+ 386	3
+ 83	4
+ 566	7
+ 496	9
+ 113	6
+ 81	3
+ 556	4
+ 557	2
+ 140	7
+ 16	5
+ 13	4
+ 487	2
+ 772	2
+ 253	10
+ 526	2
+ 384	9
+ 458	5
+ 345	0
+ 194	8
+ 941	3
+ 438	0
+ 577	10
+ 413	1
+ 196	6
+ 784	2
+ 74	8
+ 660	6
+ 967	4
+ 716	2
+ 405	2
+ 407	8
+ 154	9
+ 256	5
+ 888	4
+ 341	8
+ 757	8
+ 852	3
+ 771	3
+ 468	10
+ 819	3
+ 179	9
+ 49	8
+ 454	0
+ 271	2
+ 238	7
+ 413	6
+ 465	6
+ 509	7
+ 67	4
+ 171	4
+ 226	9
+ 186	1
+ 261	10
+ 343	7
+ 924	2
+ 982	1
+ 55	0
+ 942	5
+ 48	2
+ 679	3
+ 890	1
+ 930	4
+ 659	4
+ 75	7
+ 836	2
+ 133	1
+ 173	3
+ 141	4
+ 277	5
+ 164	2
+ 646	1
+ 305	7
+ 178	2
+ 210	2
+ 460	9
+ 512	4
+ 981	4
+ 705	6
+ 881	8
+ 366	7
+ 26	5
+ 780	2
+ 818	9
+ 634	1
+ 404	8
+ 296	0
+ 945	6
+ 751	1
+ 848	10
+ 349	3
+ 850	9
+ 658	8
+ 303	4
+ 471	2
+ 143	8
+ 902	2
+ 335	7
+ 368	2
+ 602	0
+ 248	0
+ 800	5
+ 55	7
+ 145	8
+ 868	10
+ 767	2
+ 301	6
+ 78	10
+ 447	4
+ 322	9
+ 566	5
+ 754	5
+ 633	1
+ 149	0
+ 242	8
+ 2	5
+ 757	8
+ 35	8
+ 547	2
+ 618	4
+ 174	4
+ 631	5
+ 1	7
+ 434	4
+ 91	8
+ 366	7
+ 221	1
+ 124	9
+ 208	3
+ 855	5
+ 25	9
+ 941	8
+ 660	10
+ 593	2
+ 157	2
+ 621	3
+ 596	3
+ 806	6
+ 962	2
+ 45	1
+ 996	4
+ 709	2
+ 530	8
+ 72	7
+ 107	9
+ 189	1
+ 784	1
+ 913	4
+ 106	5
+ 650	3
+ 717	3
+ 594	3
+ 524	4
+ 910	5
+ 640	10
+ 538	6
+ 365	2
+ 854	9
+ 80	9
+ 634	2
+ 852	8
+ 318	6
+ 953	2
+ 80	1
+ 737	7
+ 323	5
+ 2	9
+ 766	5
+ 317	7
+ 11	10
+ 630	5
+ 593	10
+ 795	4
+ 891	9
+ 372	5
+ 61	2
+ 348	4
+ 861	3
+ 610	9
+ 360	3
+ 672	7
+ 800	7
+ 599	6
+ 199	9
+ 242	2
+ 873	9
+ 759	5
+ 868	6
+ 912	8
+ 429	3
+ 284	5
+ 507	6
+ 869	4
+ 933	5
+ 309	3
+ 825	10
+ 976	6
+ 654	6
+ 190	9
+ 491	4
+ 63	4
+ 304	8
+ 829	2
+ 377	7
+ 931	8
+ 24	2
+ 295	5
+ 848	2
+ 899	8
+ 642	2
+ 74	5
+ 188	0
+ 92	8
+ 624	3
+ 695	1
+ 714	8
+ 479	0
+ 581	3
+ 191	10
+ 49	1
+ 763	1
+ 337	1
+ 604	2
+ 222	5
+ 965	9
+ 712	0
+ 332	9
+ 88	4
+ 742	7
+ 706	4
+ 828	4
+ 196	3
+ 438	8
+ 616	6
+ 735	7
+ 751	5
+ 738	1
+ 556	3
+ 272	8
+ 846	2
+ 643	6
+ 277	10
+ 457	4
+ 398	2
+ 77	1
+ 636	9
+ 524	8
+ 213	10
+ 609	8
+ 591	3
+ 493	3
+ 842	2
+ 430	4
+ 573	7
+ 177	4
+ 940	8
+ 977	2
+ 795	4
+ 581	2
+ 633	7
+ 297	3
+ 564	8
+ 101	8
+ 783	7
+ 605	4
+ 55	1
+ 716	9
+ 329	1
+ 296	9
+ 847	5
+ 321	8
+ 294	3
+ 3	1
+ 731	6
+ 281	4
+ 243	6
+ 634	8
+ 399	7
+ 583	2
+ 445	2
+ 555	5
+ 286	3
+ 398	6
+ 417	7
+ 517	3
+ 167	8
+ 51	5
+ 135	1
+ 549	9
+ 638	8
+ 231	9
+ 409	9
+ 687	8
+ 599	3
+ 989	0
+ 458	5
+ 545	7
+ 816	9
+ 359	2
+ 637	9
+ 496	8
+ 713	5
+ 265	8
+ 601	8
+ 715	2
+ 645	9
+ 119	1
+ 810	8
+ 862	4
+ 76	9
+ 454	5
+ 395	10
+ 279	2
+ 942	6
+ 442	6
+ 513	9
+ 383	2
+ 486	6
+ 73	1
+ 463	8
+ 325	1
+ 733	4
+ 162	5
+ 251	0
+ 952	3
+ 874	4
+ 862	3
+ 405	1
+ 479	3
+ 778	9
+ 925	3
+ 860	3
+ 516	3
+ 956	6
+ 433	4
+ 377	8
+ 527	1
+ 203	7
+ 654	5
+ 713	6
+ 781	6
+ 12	6
+ 856	4
+ 783	3
+ 763	6
+ 257	7
+ 852	1
+ 995	4
+ 463	10
+ 957	9
+ 369	3
+ 654	9
+ 445	9
+ 584	1
+ 310	3
+ 704	1
+ 884	7
+ 734	7
+ 132	5
+ 75	9
+ 79	3
+ 582	9
+ 449	6
+ 299	9
+ 527	3
+ 808	9
+ 590	5
+ 791	0
+ 318	4
+ 134	6
+ 671	8
+ 721	6
+ 554	5
+ 295	7
+ 973	4
+ 582	1
+ 702	2
+ 983	2
+ 741	3
+ 63	3
+ 538	9
+ 163	1
+ 333	10
+ 164	7
+ 329	3
+ 280	10
+ 136	0
+ 555	7
+ 455	8
+ 377	4
+ 220	10
+ 480	9
+ 122	5
+ 72	9
+ 744	1
+ 130	3
+ 6	3
+ 410	3
+ 248	6
+ 989	6
+ 872	3
+ 577	0
+ 270	1
+ 697	7
+ 981	1
+ 153	2
+ 32	6
+ 122	2
+ 96	2
+ 16	8
+ 329	1
+ 122	3
+ 440	5
+ 673	7
+ 107	7
+ 265	10
+ 932	8
+ 986	2
+ 972	7
+ 927	10
+ 757	1
+ 154	8
+ 713	3
+ 942	8
+ 470	10
+ 649	8
+ 104	8
+ 134	5
+ 305	8
+ 232	4
+ 469	5
+ 390	4
+ 338	4
+ 602	3
+ 58	5
+ 264	8
+ 609	4
+ 603	3
+ 694	5
+ 131	2
+ 503	8
+ 962	6
+ 552	1
+ 152	9
+ 902	4
+ 268	4
+ 881	7
+ 772	2
+ 33	4
+ 529	1
+ 903	8
+ 905	5
+ 210	5
+ 833	9
+ 53	10
+ 67	6
+ 744	0
+ 164	3
+ 125	3
+ 153	0
+ 699	4
+ 398	6
+ 78	2
+ 798	1
+ 544	3
+ 202	4
+ 119	1
+ 959	3
+ 615	8
+ 232	7
+ 756	3
+ 224	5
+ 328	4
+ 797	5
+ 703	10
+ 480	4
+ 371	9
+ 982	4
+ 49	8
+ 561	6
+ 106	8
+ 40	2
+ 869	10
+ 554	5
+ 790	8
+ 151	5
+ 85	4
+ 47	4
+ 763	8
+ 866	5
+ 794	3
+ 868	2
+ 225	8
+ 615	3
+ 629	2
+ 866	7
+ 937	9
+ 960	8
+ 904	5
+ 290	7
+ 301	4
+ 241	4
+ 816	3
+ 799	6
+ 131	7
+ 45	9
+ 12	9
+ 90	2
+ 762	7
+ 510	4
+ 880	4
+ 126	8
+ 282	1
+ 623	2
+ 601	9
+ 880	9
+ 354	1
+ 287	2
+ 408	1
+ 749	5
+ 753	8
+ 464	8
+ 707	6
+ 2	5
+ 258	5
+ 859	1
+ 888	10
+ 956	2
+ 71	6
+ 355	7
+ 492	2
+ 574	8
+ 355	9
+ 15	8
+ 948	8
+ 302	7
+ 558	8
+ 466	3
+ 320	5
+ 733	6
+ 980	6
+ 716	9
+ 577	7
+ 37	6
+ 251	4
+ 321	7
+ 627	9
+ 588	10
+ 756	6
+ 746	7
+ 367	0
+ 405	9
+ 814	9
+ 191	1
+ 338	9
+ 712	3
+ 517	4
+ 186	1
+ 100	2
+ 743	4
+ 615	1
+ 93	2
+ 958	7
+ 225	7
+ 284	10
+ 418	7
+ 19	8
+ 577	8
+ 693	8
+ 967	0
+ 692	7
+ 349	2
+ 106	5
+ 303	2
+ 758	0
+ 557	4
+ 109	7
+ 616	1
+ 332	8
+ 782	6
+ 812	2
+ 267	8
+ 22	8
+ 665	7
+ 612	6
+ 746	3
+ 309	1
+ 512	4
+ 630	8
+ 622	4
+ 860	2
+ 762	10
+ 830	4
+ 37	2
+ 219	8
+ 777	0
+ 19	0
+ 863	0
+ 888	5
+ 756	5
+ 159	5
+ 804	5
+ 597	3
+ 884	2
+ 131	5
+ 616	10
+ 685	4
+ 961	5
+ 756	10
+ 675	10
+ 818	5
+ 6	8
+ 496	9
+ 878	4
+ 397	6
+ 884	6
+ 135	7
+ 23	7
+ 3	9
+ 959	1
+ 412	6
+ 125	1
+ 953	1
+ 611	7
+ 84	3
+ 683	9
+ 739	7
+ 738	2
+ 559	6
+ 619	10
+ 249	5
+ 511	4
+ 190	5
+ 116	2
+ 442	1
+ 327	9
+ 649	5
+ 951	6
+ 538	6
+ 310	6
+ 848	10
+ 524	6
+ 684	3
+ 822	2
+ 878	4
+ 198	1
+ 943	7
+ 512	1
+ 244	6
+ 325	7
+ 702	7
+ 539	4
+ 104	5
+ 952	6
+ 52	3
+ 264	9
+ 257	8
+ 487	9
+ 50	3
+ 183	9
+ 748	4
+ 56	7
+ 91	6
+ 823	3
+ 195	1
+ 21	9
+ 801	6
+ 247	9
+ 50	2
+ 546	1
+ 462	8
+ 2	7
+ 597	5
+ 659	6
+ 797	8
+ 575	5
+ 224	6
+ 236	3
+ 198	1
+ 650	4
+ 208	7
+ 289	0
+ 231	5
+ 913	3
+ 735	5
+ 383	2
+ 268	4
+ 915	9
+ 874	6
+ 512	7
+ 417	1
+ 215	6
+ 718	5
+ 955	9
+ 511	6
+ 309	7
+ 275	6
+ 727	5
+ 133	6
+ 786	9
+ 99	2
+ 64	4
+ 554	10
+ 233	4
+ 554	7
+ 98	10
+ 832	3
+ 611	5
+ 765	6
+ 466	3
+ 170	8
+ 995	4
+ 371	7
+ 951	5
+ 363	7
+ 371	5
+ 907	4
+ 830	5
+ 414	1
+ 889	10
+ 808	10
+ 937	6
+ 301	5
+ 189	1
+ 114	7
+ 343	3
+ 429	3
+ 729	8
+ 61	7
+ 304	4
+ 416	7
+ 886	3
+ 110	7
+ 784	5
+ 779	7
+ 491	6
+ 660	4
+ 226	10
+ 976	4
+ 28	1
+ 71	4
+ 374	5
+ 709	1
+ 300	8
+ 782	6
+ 193	2
+ 280	1
+ 521	4
+ 794	3
+ 913	6
+ 978	4
+ 159	6
+ 833	4
+ 600	8
+ 801	6
+ 899	9
+ 999	3
+ 371	7
+ 376	7
+ 477	2
+ 276	7
+ 356	6
+ 749	9
+ 945	5
+ 183	9
+ 116	2
+ 262	3
+ 799	1
+ 661	4
+ 904	5
+ 28	8
+ 334	0
+ 76	7
+ 735	5
+ 376	2
+ 609	7
+ 882	10
+ 207	6
+ 843	2
+ 174	0
+ 10	3
+ 187	3
+ 565	10
+ 366	2
+ 386	3
+ 689	4
+ 73	0
+ 441	1
+ 727	2
+ 600	1
+ 388	2
+ 756	3
+ 176	10
+ 901	0
+ 115	1
+ 45	1
+ 364	2
+ 396	9
+ 218	8
+ 156	6
+ 32	8
+ 18	1
+ 867	5
+ 254	6
+ 635	9
+ 699	0
+ 65	5
+ 293	2
+ 417	2
+ 259	5
+ 268	3
+ 656	6
+ 535	1
+ 562	8
+ 814	7
+ 357	8
+ 563	4
+ 952	4
+ 834	2
+ 25	5
+ 60	7
+ 492	1
+ 178	8
+ 365	6
+ 977	6
+ 127	2
+ 928	8
+ 877	5
+ 834	4
+ 216	6
+ 157	6
+ 495	7
+ 949	4
+ 150	8
+ 653	2
+ 252	7
+ 898	7
+ 838	1
+ 527	2
+ 671	5
+ 827	8
+ 750	8
+ 581	6
+ 217	4
+ 66	4
+ 64	2
+ 7	6
+ 943	10
+ 6	1
+ 738	7
+ 267	10
+ 372	2
+ 733	2
+ 242	3
+ 413	9
+ 765	2
+ 712	5
+ 994	3
+ 142	2
+ 708	2
+ 645	8
+ 431	7
+ 331	4
+ 608	3
+ 466	3
+ 996	7
+ 336	4
+ 899	1
+ 577	1
+ 330	10
+ 54	1
+ 229	8
+ 609	2
+ 59	8
+ 435	8
+ 959	1
+ 539	4
+ 733	9
+ 763	3
+ 207	2
+ 687	2
+ 961	0
+ 570	9
+ 93	1
+ 1	4
+ 137	1
+ 517	4
+ 821	1
+ 590	9
+ 878	0
+ 646	8
+ 106	2
+ 226	8
+ 56	10
+ 180	3
+ 218	9
+ 466	2
+ 891	0
+ 39	10
+ 183	0
+ 407	3
+ 95	9
+ 686	9
+ 51	3
+ 795	9
+ 301	4
+ 765	4
+ 627	10
+ 246	7
+ 981	4
+ 946	2
+ 294	4
+ 378	2
+ 448	4
+ 170	6
+ 457	6
+ 950	6
+ 501	6
+ 467	6
+ 911	3
+ 480	2
+ 704	2
+ 619	3
+ 237	9
+ 14	2
+ 291	10
+ 416	6
+ 372	8
+ 770	8
+ 212	9
+ 451	7
+ 516	4
+ 221	0
+ 37	7
+ 568	9
+ 950	0
+ 160	7
+ 293	8
+ 985	5
+ 644	10
+ 747	9
+ 960	2
+ 519	3
+ 958	3
+ 151	2
+ 227	6
+ 838	7
+ 3	1
+ 760	0
+ 747	3
+ 988	7
+ 376	1
+ 351	7
+ 928	3
+ 198	6
+ 336	9
+ 506	3
+ 109	0
+ 627	1
+ 312	8
+ 236	5
+ 380	1
+ 283	4
+ 133	0
+ 423	9
+ 371	4
+ 577	7
+ 559	9
+ 415	5
+ 264	6
+ 58	6
+ 559	6
+ 896	7
+ 588	5
+ 734	9
+ 302	10
+ 440	7
+ 45	7
+ 66	2
+ 766	5
+ 58	1
+ 899	6
+ 883	5
+ 562	3
+ 945	8
+ 911	0
+ 427	5
+ 566	3
+ 138	2
+ 847	9
+ 55	1
+ 842	5
+ 832	9
+ 218	9
+ 66	10
+ 386	1
+ 120	3
+ 758	0
+ 743	3
+ 300	7
+ 147	2
+ 690	6
+ 681	3
+ 898	8
+ 411	7
+ 691	5
+ 895	5
+ 960	7
+ 420	2
+ 625	5
+ 162	0
+ 610	3
+ 296	4
+ 283	0
+ 688	6
+ 726	8
+ 794	4
+ 410	5
+ 673	3
+ 294	1
+ 54	10
+ 549	9
+ 518	5
+ 677	9
+ 687	3
+ 425	8
+ 314	0
+ 130	6
+ 402	4
+ 648	1
+ 997	4
+ 926	8
+ 791	3
+ 267	5
+ 645	6
+ 547	7
+ 546	1
+ 649	1
+ 605	3
+ 3	3
+ 628	4
+ 141	9
+ 462	3
+ 551	9
+ 684	2
+ 954	7
+ 574	9
+ 472	4
+ 217	7
+ 828	9
+ 299	4
+ 562	8
+ 471	2
+ 909	1
+ 536	9
+ 367	2
+ 339	5
+ 106	8
+ 779	7
+ 664	5
+ 856	6
+ 144	4
+ 499	6
+ 796	7
+ 353	6
+ 579	7
+ 999	1
+ 497	5
+ 351	4
+ 546	9
+ 317	9
+ 51	7
+ 421	2
+ 456	2
+ 813	1
+ 664	7
+ 738	8
+ 100	2
+ 422	9
+ 953	8
+ 520	5
+ 428	5
+ 672	9
+ 990	0
+ 331	5
+ 910	6
+ 448	10
+ 305	9
+ 118	8
+ 70	9
+ 881	7
+ 601	6
+ 541	7
+ 855	10
+ 597	8
+ 739	1
+ 341	2
+ 637	0
+ 93	6
+ 37	4
+ 162	9
+ 73	6
+ 908	4
+ 480	0
+ 139	6
+ 957	0
+ 284	6
+ 638	8
+ 259	5
+ 788	9
+ 302	5
+ 974	6
+ 695	6
+ 656	8
+ 237	7
+ 212	4
+ 639	3
+ 9	5
+ 663	5
+ 573	8
+ 39	5
+ 821	3
+ 88	5
+ 148	3
+ 952	9
+ 204	3
+ 464	2
+ 896	2
+ 789	6
+ 947	0
+ 244	2
+ 425	9
+ 444	4
+ 430	1
+ 924	0
+ 909	10
+ 533	7
+ 286	6
+ 189	4
+ 969	1
+ 370	2
+ 394	8
+ 350	3
+ 993	1
+ 842	9
+ 165	1
+ 99	6
+ 969	5
+ 24	4
+ 651	9
+ 401	6
+ 911	9
+ 290	2
+ 556	5
+ 631	5
+ 619	0
+ 696	0
+ 835	0
+ 303	8
+ 185	1
+ 767	3
+ 231	9
+ 940	2
+ 410	10
+ 598	1
+ 912	10
+ 621	8
+ 934	9
+ 20	5
+ 389	7
+ 14	0
+ 651	7
+ 22	5
+ 757	3
+ 313	9
+ 471	1
+ 292	7
+ 947	2
+ 902	4
+ 196	5
+ 418	1
+ 500	0
+ 931	4
+ 949	10
+ 924	3
+ 601	9
+ 348	3
+ 648	4
+ 738	4
+ 695	1
+ 347	2
+ 132	6
+ 867	1
+ 872	8
+ 436	1
+ 269	9
+ 176	8
+ 893	1
+ 203	8
+ 59	1
+ 181	7
+ 65	5
+ 912	7
+ 898	7
+ 118	6
+ 702	5
+ 758	8
+ 105	6
+ 913	10
+ 395	3
+ 45	7
+ 204	2
+ 433	1
+ 329	6
+ 939	4
+ 764	1
+ 48	8
+ 650	10
+ 542	5
+ 610	7
+ 141	3
+ 126	9
+ 146	2
+ 525	1
+ 208	9
+ 409	3
+ 584	6
+ 474	0
+ 710	8
+ 654	6
+ 190	4
+ 770	2
+ 247	4
+ 198	8
+ 968	8
+ 448	1
+ 121	6
+ 8	3
+ 805	5
+ 326	0
+ 452	7
+ 265	0
+ 347	7
+ 53	1
+ 542	7
+ 706	7
+ 124	5
+ 970	4
+ 896	2
+ 159	9
+ 977	6
+ 972	1
+ 182	10
+ 364	10
+ 513	7
+ 999	10
+ 425	3
+ 1000	8
+ 3	1
+ 829	5
+ 759	5
+ 277	9
+ 12	2
+ 254	9
+ 415	4
+ 772	4
+ 21	7
+ 490	2
+ 725	9
+ 189	2
+ 544	2
+ 202	10
+ 452	2
+ 741	5
+ 254	6
+ 1000	0
+ 106	3
+ 896	1
+ 523	1
+ 27	9
+ 563	8
+ 330	6
+ 544	8
+ 786	3
+ 674	10
+ 506	2
+ 162	7
+ 186	6
+ 910	9
+ 69	2
+ 496	1
+ 177	6
+ 346	1
+ 720	9
+ 223	7
+ 807	8
+ 546	1
+ 369	1
+ 958	2
+ 358	6
+ 129	9
+ 849	3
+ 573	0
+ 906	5
+ 961	10
+ 646	5
+ 45	8
+ 59	4
+ 896	8
+ 259	1
+ 526	1
+ 904	1
+ 204	3
+ 162	2
+ 428	5
+ 793	6
+ 385	6
+ 849	10
+ 676	8
+ 440	6
+ 731	1
+ 94	8
+ 909	2
+ 166	8
+ 933	4
+ 923	5
+ 492	8
+ 531	7
+ 100	7
+ 858	5
+ 214	7
+ 86	6
+ 292	9
+ 556	10
+ 691	10
+ 604	4
+ 82	7
+ 197	10
+ 851	4
+ 796	8
+ 788	7
+ 243	3
+ 547	8
+ 975	6
+ 467	8
+ 176	7
+ 484	3
+ 279	8
+ 198	8
+ 743	9
+ 832	3
+ 310	9
+ 46	5
+ 906	9
+ 871	7
+ 681	7
+ 422	9
+ 938	10
+ 698	9
+ 615	2
+ 747	8
+ 846	2
+ 53	1
+ 6	3
+ 961	7
+ 139	8
+ 97	4
+ 707	1
+ 957	6
+ 40	8
+ 314	7
+ 487	7
+ 645	4
+ 704	3
+ 339	3
+ 508	1
+ 110	4
+ 315	2
+ 479	3
+ 414	4
+ 70	6
+ 231	2
+ 3	9
+ 311	10
+ 550	4
+ 788	9
+ 72	3
+ 600	7
+ 700	3
+ 60	0
+ 623	6
+ 124	7
+ 922	4
+ 897	4
+ 760	3
+ 839	8
+ 864	1
+ 998	9
+ 9	3
+ 827	6
+ 660	6
+ 423	7
+ 891	0
+ 450	6
+ 327	5
+ 630	10
+ 78	8
+ 685	0
+ 194	6
+ 401	10
+ 893	2
+ 785	8
+ 311	8
+ 625	3
+ 92	5
+ 878	8
+ 68	3
+ 484	10
+ 325	9
+ 550	7
+ 444	2
+ 603	5
+ 935	3
+ 522	1
+ 870	9
+ 82	8
+ 163	9
+ 521	5
+ 650	1
+ 794	7
+ 598	7
+ 494	7
+ 974	10
+ 625	3
+ 911	2
+ 951	4
+ 356	6
+ 877	3
+ 842	4
+ 419	7
+ 322	5
+ 476	5
+ 369	10
+ 960	0
+ 143	8
+ 761	7
+ 426	3
+ 408	4
+ 233	0
+ 698	1
+ 209	6
+ 499	6
+ 203	4
+ 856	0
+ 775	3
+ 757	1
+ 776	2
+ 583	1
+ 229	5
+ 164	4
+ 297	9
+ 114	7
+ 180	5
+ 122	4
+ 555	8
+ 556	8
+ 469	1
+ 328	7
+ 431	2
+ 717	2
+ 459	5
+ 302	2
+ 706	9
+ 380	9
+ 428	5
+ 308	7
+ 468	4
+ 447	6
+ 944	6
+ 60	5
+ 390	6
+ 262	9
+ 672	6
+ 531	1
+ 774	2
+ 307	2
+ 721	6
+ 468	4
+ 495	8
+ 363	9
+ 392	7
+ 648	9
+ 93	1
+ 508	0
+ 664	6
+ 536	1
+ 185	8
+ 913	9
+ 390	4
+ 959	2
+ 692	3
+ 397	4
+ 877	9
+ 841	4
+ 713	2
+ 295	1
+ 875	9
+ 965	10
+ 37	5
+ 6	7
+ 42	5
+ 755	2
+ 342	7
+ 84	7
+ 112	0
+ 895	8
+ 310	3
+ 218	2
+ 158	1
+ 559	9
+ 264	9
+ 976	1
+ 796	9
+ 108	8
+ 413	1
+ 533	5
+ 658	3
+ 682	10
+ 956	8
+ 731	1
+ 809	6
+ 873	1
+ 918	1
+ 307	1
+ 152	9
+ 947	4
+ 719	9
+ 555	5
+ 863	7
+ 347	3
+ 778	9
+ 731	4
+ 168	4
+ 435	1
+ 179	2
+ 193	10
+ 791	1
+ 108	7
+ 159	4
+ 786	3
+ 280	7
+ 726	10
+ 655	3
+ 512	5
+ 944	9
+ 793	7
+ 739	5
+ 158	9
+ 937	6
+ 32	1
+ 758	2
+ 104	5
+ 292	2
+ 259	5
+ 626	0
+ 761	9
+ 777	5
+ 903	4
+ 767	4
+ 949	7
+ 274	7
+ 434	0
+ 266	6
+ 921	2
+ 184	10
+ 318	9
+ 178	4
+ 491	5
+ 633	8
+ 921	3
+ 795	7
+ 164	6
+ 168	1
+ 3	9
+ 483	10
+ 647	8
+ 694	1
+ 771	10
+ 673	7
+ 163	9
+ 644	5
+ 799	8
+ 903	3
+ 292	5
+ 40	2
+ 794	8
+ 895	10
+ 407	1
+ 25	4
+ 999	5
+ 362	6
+ 265	1
+ 727	0
+ 16	4
+ 727	2
+ 257	4
+ 660	1
+ 193	6
+ 345	5
+ 98	4
+ 698	9
+ 221	6
+ 850	6
+ 656	9
+ 37	7
+ 383	4
+ 301	6
+ 455	0
+ 684	5
+ 428	4
+ 650	7
+ 781	3
+ 740	10
+ 872	1
+ 459	10
+ 471	2
+ 863	7
+ 749	7
+ 319	4
+ 589	4
+ 59	10
+ 755	4
+ 621	2
+ 388	3
+ 681	8
+ 716	3
+ 501	5
+ 641	2
+ 471	5
+ 327	9
+ 484	8
+ 87	3
+ 490	8
+ 60	8
+ 241	6
+ 166	3
+ 622	9
+ 661	2
+ 132	0
+ 547	8
+ 865	3
+ 144	4
+ 760	8
+ 606	2
+ 299	9
+ 162	8
+ 731	2
+ 130	2
+ 85	2
+ 30	3
+ 840	2
+ 627	5
+ 117	3
+ 705	2
+ 337	3
+ 61	2
+ 515	2
+ 566	3
+ 991	2
+ 506	3
+ 105	7
+ 74	8
+ 916	2
+ 57	0
+ 395	1
+ 328	2
+ 282	10
+ 697	4
+ 243	4
+ 647	6
+ 654	7
+ 781	2
+ 914	3
+ 444	9
+ 520	9
+ 196	6
+ 618	3
+ 461	5
+ 474	5
+ 535	9
+ 604	9
+ 105	9
+ 818	8
+ 285	1
+ 205	9
+ 641	9
+ 641	4
+ 28	6
+ 768	5
+ 461	3
+ 421	7
+ 913	0
+ 927	4
+ 573	4
+ 892	1
+ 271	5
+ 971	4
+ 382	8
+ 179	7
+ 851	4
+ 600	5
+ 243	2
+ 913	3
+ 796	7
+ 742	3
+ 969	2
+ 914	9
+ 202	8
+ 257	8
+ 243	1
+ 882	5
+ 644	9
+ 891	0
+ 643	1
+ 694	5
+ 454	3
+ 986	7
+ 535	9
+ 967	3
+ 580	7
+ 588	5
+ 871	5
+ 432	1
+ 344	7
+ 847	6
+ 837	7
+ 100	5
+ 583	10
+ 508	2
+ 61	2
+ 721	5
+ 497	7
+ 211	0
+ 606	2
+ 363	2
+ 887	10
+ 736	8
+ 454	2
+ 831	8
+ 858	7
+ 384	7
+ 408	5
+ 176	10
+ 475	7
+ 218	5
+ 887	9
+ 51	4
+ 646	3
+ 415	3
+ 440	8
+ 438	3
+ 729	2
+ 86	2
+ 344	9
+ 981	2
+ 596	4
+ 896	0
+ 850	1
+ 995	3
+ 756	2
+ 861	6
+ 152	9
+ 26	8
+ 174	4
+ 50	6
+ 218	5
+ 942	9
+ 663	0
+ 131	0
+ 944	1
+ 208	5
+ 477	1
+ 544	3
+ 176	5
+ 652	9
+ 752	5
+ 574	9
+ 424	6
+ 702	6
+ 41	8
+ 212	3
+ 241	2
+ 207	9
+ 181	3
+ 911	1
+ 450	1
+ 665	9
+ 222	2
+ 254	4
+ 748	9
+ 329	5
+ 418	9
+ 405	8
+ 504	1
+ 441	5
+ 860	7
+ 803	1
+ 807	0
+ 4	10
+ 348	9
+ 114	8
+ 33	8
+ 725	3
+ 988	10
+ 653	7
+ 885	10
+ 238	3
+ 886	6
+ 146	4
+ 751	6
+ 934	6
+ 240	7
+ 712	0
+ 748	7
+ 35	1
+ 631	1
+ 894	7
+ 928	6
+ 920	9
+ 598	6
+ 654	5
+ 556	9
+ 786	4
+ 535	9
+ 832	3
+ 518	8
+ 896	8
+ 504	6
+ 804	3
+ 324	8
+ 347	10
+ 989	2
+ 619	9
+ 860	5
+ 834	5
+ 113	5
+ 942	7
+ 380	7
+ 112	9
+ 659	9
+ 200	2
+ 711	1
+ 934	2
+ 704	7
+ 466	0
+ 578	8
+ 983	6
+ 55	6
+ 485	9
+ 142	3
+ 373	3
+ 808	3
+ 925	2
+ 43	0
+ 102	7
+ 981	3
+ 878	7
+ 398	8
+ 906	1
+ 551	4
+ 129	1
+ 186	1
+ 697	2
+ 715	2
+ 156	9
+ 502	5
+ 112	3
+ 844	0
+ 497	9
+ 74	6
+ 589	1
+ 900	5
+ 747	3
+ 280	7
+ 399	8
+ 26	5
+ 961	2
+ 641	7
+ 453	4
+ 840	6
+ 212	3
+ 138	3
+ 651	10
+ 362	1
+ 869	4
+ 746	5
+ 490	6
+ 925	2
+ 943	2
+ 890	3
+ 36	9
+ 870	10
+ 128	5
+ 655	6
+ 866	5
+ 190	1
+ 837	3
+ 403	5
+ 310	8
+ 636	2
+ 200	4
+ 637	7
+ 28	6
+ 927	10
+ 766	8
+ 313	8
+ 733	2
+ 798	9
+ 695	5
+ 443	6
+ 948	6
+ 640	8
+ 960	0
+ 274	3
+ 808	9
+ 449	0
+ 292	1
+ 698	3
+ 648	6
+ 291	4
+ 443	6
+ 215	2
+ 788	0
+ 37	5
+ 467	5
+ 44	4
+ 112	7
+ 200	1
+ 727	5
+ 342	5
+ 383	8
+ 542	7
+ 877	2
+ 995	5
+ 866	3
+ 938	3
+ 891	2
+ 484	7
+ 167	5
+ 162	6
+ 1	2
+ 48	1
+ 890	2
+ 186	6
+ 721	5
+ 151	1
+ 318	7
+ 779	2
+ 934	8
+ 719	8
+ 61	7
+ 108	10
+ 810	6
+ 632	10
+ 114	8
+ 610	1
+ 0	7
+ 229	9
+ 906	4
+ 506	6
+ 942	7
+ 731	3
+ 350	5
+ 455	3
+ 284	2
+ 83	3
+ 830	2
+ 297	6
+ 783	9
+ 617	9
+ 723	2
+ 12	7
+ 885	2
+ 614	8
+ 656	1
+ 418	6
+ 777	1
+ 858	1
+ 659	3
+ 411	9
+ 486	5
+ 288	3
+ 685	6
+ 957	5
+ 514	6
+ 365	2
+ 801	4
+ 961	7
+ 618	6
+ 477	3
+ 695	9
+ 871	5
+ 44	7
+ 600	7
+ 42	0
+ 646	5
+ 504	9
+ 845	2
+ 520	8
+ 657	0
+ 375	0
+ 272	2
+ 398	2
+ 862	0
+ 809	3
+ 290	5
+ 234	2
+ 976	3
+ 891	6
+ 982	9
+ 587	6
+ 461	1
+ 563	3
+ 280	1
+ 107	9
+ 117	5
+ 958	4
+ 658	4
+ 623	5
+ 372	4
+ 859	7
+ 935	1
+ 823	9
+ 372	7
+ 488	4
+ 646	1
+ 982	1
+ 165	5
+ 412	4
+ 628	5
+ 382	7
+ 2	3
+ 135	7
+ 696	8
+ 179	1
+ 190	0
+ 730	1
+ 131	6
+ 36	5
+ 266	5
+ 857	9
+ 598	8
+ 19	8
+ 384	4
+ 209	0
+ 951	6
+ 759	10
+ 932	9
+ 612	6
+ 652	8
+ 696	8
+ 830	4
+ 966	10
+ 978	0
+ 464	2
+ 527	3
+ 155	1
+ 160	2
+ 889	5
+ 605	1
+ 556	6
+ 690	3
+ 508	6
+ 209	1
+ 249	9
+ 912	9
+ 703	7
+ 370	7
+ 703	3
+ 672	2
+ 591	2
+ 488	7
+ 324	6
+ 921	2
+ 191	5
+ 311	7
+ 82	0
+ 62	6
+ 621	3
+ 710	9
+ 132	6
+ 815	8
+ 364	2
+ 504	1
+ 533	2
+ 234	1
+ 374	7
+ 872	7
+ 369	8
+ 911	6
+ 319	2
+ 307	4
+ 221	4
+ 990	8
+ 641	7
+ 713	8
+ 323	5
+ 608	7
+ 714	1
+ 755	2
+ 288	10
+ 372	7
+ 711	2
+ 360	1
+ 36	3
+ 640	4
+ 492	9
+ 755	7
+ 317	7
+ 556	10
+ 446	3
+ 731	8
+ 798	3
+ 457	5
+ 450	2
+ 760	7
+ 201	1
+ 400	9
+ 375	8
+ 991	4
+ 31	6
+ 765	5
+ 578	5
+ 237	9
+ 265	8
+ 852	7
+ 63	6
+ 481	9
+ 921	9
+ 374	4
+ 149	1
+ 109	3
+ 265	5
+ 261	6
+ 270	3
+ 50	3
+ 883	8
+ 824	5
+ 335	1
+ 355	6
+ 854	2
+ 312	9
+ 789	8
+ 778	7
+ 730	2
+ 81	9
+ 285	2
+ 228	6
+ 700	5
+ 190	10
+ 740	2
+ 271	6
+ 55	1
+ 84	4
+ 156	4
+ 990	0
+ 646	3
+ 927	4
+ 94	7
+ 145	8
+ 856	2
+ 702	1
+ 417	9
+ 692	1
+ 418	9
+ 87	2
+ 122	4
+ 782	2
+ 453	9
+ 567	6
+ 305	6
+ 619	10
+ 859	5
+ 386	10
+ 250	5
+ 776	1
+ 757	5
+ 249	2
+ 407	9
+ 291	8
+ 823	4
+ 983	9
+ 736	8
+ 121	2
+ 631	7
+ 798	9
+ 245	4
+ 886	1
+ 963	3
+ 57	2
+ 803	8
+ 320	6
+ 310	6
+ 734	7
+ 509	0
+ 543	3
+ 403	5
+ 276	1
+ 291	4
+ 328	9
+ 85	1
+ 858	3
+ 544	7
+ 434	5
+ 16	5
+ 719	8
+ 324	0
+ 378	6
+ 607	1
+ 352	1
+ 137	9
+ 447	5
+ 420	7
+ 679	7
+ 119	0
+ 634	2
+ 134	5
+ 535	7
+ 236	10
+ 184	3
+ 461	9
+ 71	8
+ 942	4
+ 418	5
+ 561	8
+ 664	7
+ 664	1
+ 238	1
+ 834	9
+ 796	10
+ 924	4
+ 158	1
+ 921	7
+ 735	2
+ 662	9
+ 409	1
+ 822	5
+ 907	8
+ 929	3
+ 312	5
+ 95	10
+ 188	8
+ 87	4
+ 844	9
+ 342	6
+ 874	3
+ 69	0
+ 324	10
+ 724	1
+ 148	4
+ 977	6
+ 510	8
+ 38	4
+ 563	10
+ 743	9
+ 458	8
+ 851	6
+ 598	9
+ 72	4
+ 859	4
+ 81	7
+ 680	2
+ 764	0
+ 141	5
+ 63	3
+ 875	0
+ 846	4
+ 839	9
+ 801	4
+ 851	5
+ 277	3
+ 382	1
+ 955	10
+ 65	0
+ 421	9
+ 441	5
+ 656	1
+ 653	4
+ 125	8
+ 908	2
+ 83	8
+ 228	9
+ 167	1
+ 813	10
+ 469	7
+ 513	7
+ 974	9
+ 873	9
+ 875	9
+ 955	3
+ 862	4
+ 799	5
+ 517	5
+ 939	6
+ 245	8
+ 830	3
+ 630	1
+ 257	8
+ 126	1
+ 765	6
+ 734	3
+ 341	7
+ 173	2
+ 637	0
+ 152	6
+ 344	0
+ 988	1
+ 533	5
+ 594	5
+ 148	8
+ 319	10
+ 166	9
+ 37	4
+ 745	2
+ 493	5
+ 757	2
+ 788	1
+ 935	10
+ 311	6
+ 9	5
+ 164	4
+ 478	2
+ 495	0
+ 658	1
+ 483	8
+ 927	8
+ 785	1
+ 751	8
+ 516	5
+ 984	0
+ 7	7
+ 235	8
+ 840	2
+ 756	2
+ 742	8
+ 615	9
+ 118	1
+ 58	6
+ 104	7
+ 700	6
+ 522	6
+ 389	3
+ 720	1
+ 128	2
+ 637	1
+ 244	6
+ 854	5
+ 439	7
+ 650	2
+ 845	4
+ 961	5
+ 298	1
+ 552	4
+ 690	7
+ 72	4
+ 243	6
+ 18	6
+ 901	7
+ 772	0
+ 973	4
+ 142	2
+ 52	10
+ 695	5
+ 691	3
+ 687	5
+ 737	6
+ 995	0
+ 725	5
+ 392	4
+ 203	5
+ 806	4
+ 59	8
+ 77	10
+ 562	8
+ 989	5
+ 258	1
+ 751	3
+ 127	4
+ 802	8
+ 792	5
+ 353	5
+ 136	3
+ 564	9
+ 895	10
+ 278	1
+ 420	1
+ 544	5
+ 908	6
+ 438	5
+ 471	4
+ 5	7
+ 558	8
+ 40	7
+ 203	8
+ 503	10
+ 331	9
+ 523	5
+ 205	1
+ 330	1
+ 42	6
+ 199	5
+ 692	7
+ 941	6
+ 363	4
+ 70	8
+ 806	1
+ 563	4
+ 831	6
+ 49	0
+ 445	6
+ 28	8
+ 408	6
+ 245	6
+ 638	6
+ 713	7
+ 182	9
+ 142	9
+ 654	1
+ 473	0
+ 463	5
+ 852	3
+ 619	4
+ 632	4
+ 18	7
+ 484	5
+ 233	5
+ 240	6
+ 63	5
+ 254	7
+ 59	10
+ 380	2
+ 880	5
+ 114	5
+ 606	6
+ 551	1
+ 131	4
+ 337	7
+ 818	10
+ 199	8
+ 650	7
+ 299	9
+ 195	5
+ 524	3
+ 23	8
+ 958	1
+ 746	3
+ 322	6
+ 860	4
+ 159	5
+ 23	7
+ 535	2
+ 114	9
+ 904	9
+ 842	1
+ 767	5
+ 786	1
+ 375	10
+ 604	9
+ 239	6
+ 678	2
+ 708	4
+ 534	0
+ 49	4
+ 466	2
+ 861	5
+ 919	4
+ 643	0
+ 269	5
+ 964	1
+ 650	7
+ 603	4
+ 797	10
+ 418	4
+ 877	7
+ 28	6
+ 853	7
+ 979	4
+ 766	0
+ 782	2
+ 236	6
+ 721	2
+ 40	4
+ 188	3
+ 911	2
+ 419	6
+ 884	0
+ 999	7
+ 1000	4
+ 82	9
+ 73	1
+ 432	9
+ 846	4
+ 313	6
+ 439	1
+ 844	7
+ 739	6
+ 831	8
+ 929	0
+ 87	8
+ 172	5
+ 402	1
+ 528	4
+ 736	5
+ 817	8
+ 405	9
+ 927	8
+ 817	8
+ 249	1
+ 384	7
+ 225	2
+ 364	10
+ 793	2
+ 742	7
+ 215	8
+ 562	4
+ 336	10
+ 443	9
+ 365	2
+ 392	2
+ 997	8
+ 72	9
+ 635	9
+ 697	9
+ 19	1
+ 573	2
+ 309	9
+ 208	1
+ 132	10
+ 824	3
+ 780	4
+ 734	1
+ 351	2
+ 980	7
+ 356	4
+ 897	4
+ 170	10
+ 276	8
+ 858	10
+ 690	9
+ 54	3
+ 121	4
+ 199	3
+ 466	3
+ 280	3
+ 678	1
+ 677	4
+ 175	0
+ 589	2
+ 743	9
+ 527	6
+ 297	7
+ 610	6
+ 502	5
+ 547	2
+ 345	6
+ 454	5
+ 965	7
+ 795	4
+ 983	1
+ 721	7
+ 135	4
+ 74	3
+ 425	7
+ 465	2
+ 607	10
+ 808	9
+ 689	4
+ 478	2
+ 886	0
+ 382	2
+ 626	8
+ 697	6
+ 488	5
+ 21	5
+ 567	7
+ 133	7
+ 140	2
+ 12	6
+ 869	5
+ 734	5
+ 469	5
+ 381	2
+ 960	9
+ 349	8
+ 884	7
+ 77	5
+ 567	8
+ 100	1
+ 266	1
+ 527	8
+ 864	7
+ 535	0
+ 867	5
+ 570	7
+ 24	3
+ 213	5
+ 845	6
+ 651	8
+ 453	0
+ 651	3
+ 732	7
+ 846	3
+ 501	9
+ 355	8
+ 67	9
+ 600	9
+ 542	1
+ 935	4
+ 682	5
+ 146	7
+ 808	4
+ 199	7
+ 953	9
+ 459	4
+ 851	1
+ 743	6
+ 837	6
+ 882	3
+ 534	2
+ 105	6
+ 118	7
+ 532	7
+ 840	5
+ 70	5
+ 971	2
+ 228	8
+ 575	4
+ 433	5
+ 277	9
+ 935	1
+ 1	7
+ 710	8
+ 266	6
+ 176	8
+ 828	3
+ 402	9
+ 986	9
+ 607	8
+ 399	7
+ 348	4
+ 892	6
+ 150	5
+ 1	6
+ 996	3
+ 474	9
+ 406	5
+ 609	1
+ 312	9
+ 708	5
+ 676	5
+ 768	1
+ 483	8
+ 10	1
+ 580	4
+ 766	9
+ 780	7
+ 502	9
+ 125	5
+ 513	1
+ 782	10
+ 52	2
+ 461	7
+ 304	8
+ 535	0
+ 261	2
+ 548	0
+ 288	0
+ 783	3
+ 121	4
+ 708	9
+ 290	5
+ 545	8
+ 418	7
+ 296	9
+ 791	1
+ 918	8
+ 266	4
+ 504	6
+ 153	0
+ 581	4
+ 250	1
+ 443	5
+ 161	2
+ 836	3
+ 589	5
+ 169	9
+ 32	7
+ 671	4
+ 384	10
+ 381	2
+ 45	3
+ 19	3
+ 678	5
+ 881	8
+ 561	5
+ 244	8
+ 591	7
+ 349	8
+ 913	2
+ 34	5
+ 728	2
+ 380	8
+ 916	1
+ 209	3
+ 18	6
+ 476	1
+ 890	5
+ 374	6
+ 17	3
+ 399	6
+ 716	6
+ 389	3
+ 330	7
+ 60	2
+ 922	1
+ 744	6
+ 296	1
+ 409	2
+ 174	6
+ 513	2
+ 209	10
+ 255	1
+ 483	6
+ 667	5
+ 883	1
+ 78	6
+ 708	5
+ 907	0
+ 204	10
+ 280	1
+ 60	0
+ 775	4
+ 147	2
+ 569	3
+ 803	1
+ 512	0
+ 71	8
+ 111	6
+ 396	8
+ 53	3
+ 842	1
+ 878	6
+ 597	8
+ 588	8
+ 751	9
+ 927	8
+ 891	7
+ 169	0
+ 886	7
+ 359	7
+ 820	9
+ 701	9
+ 638	8
+ 445	0
+ 588	5
+ 312	4
+ 628	2
+ 981	2
+ 975	6
+ 26	7
+ 437	10
+ 538	3
+ 655	7
+ 366	5
+ 445	7
+ 229	3
+ 595	9
+ 156	2
+ 741	6
+ 266	3
+ 98	6
+ 760	7
+ 768	7
+ 952	7
+ 310	10
+ 469	7
+ 931	0
+ 74	6
+ 713	4
+ 127	2
+ 164	4
+ 423	8
+ 286	6
+ 992	0
+ 180	3
+ 357	3
+ 837	1
+ 5	6
+ 858	10
+ 348	2
+ 935	8
+ 915	9
+ 823	10
+ 452	5
+ 429	6
+ 694	6
+ 935	1
+ 352	2
+ 696	3
+ 249	9
+ 602	6
+ 153	4
+ 722	2
+ 44	6
+ 115	4
+ 748	0
+ 208	7
+ 915	0
+ 652	4
+ 566	1
+ 946	3
+ 673	9
+ 377	0
+ 102	1
+ 369	4
+ 948	10
+ 956	1
+ 409	7
+ 259	5
+ 258	4
+ 844	0
+ 423	1
+ 669	3
+ 82	3
+ 705	6
+ 402	7
+ 908	1
+ 533	3
+ 101	6
+ 357	5
+ 986	3
+ 440	9
+ 406	8
+ 620	7
+ 303	9
+ 39	1
+ 885	5
+ 199	6
+ 801	3
+ 875	5
+ 929	3
+ 157	8
+ 353	7
+ 123	5
+ 325	5
+ 923	3
+ 785	4
+ 252	2
+ 213	9
+ 857	5
+ 751	9
+ 663	6
+ 359	9
+ 190	2
+ 142	1
+ 665	1
+ 343	8
+ 909	7
+ 513	0
+ 149	8
+ 513	1
+ 148	3
+ 435	4
+ 489	6
+ 273	3
+ 163	0
+ 243	8
+ 660	6
+ 687	8
+ 761	8
+ 914	4
+ 901	3
+ 249	8
+ 952	8
+ 843	1
+ 600	4
+ 173	7
+ 653	6
+ 149	1
+ 255	4
+ 489	4
+ 446	7
+ 244	1
+ 334	9
+ 955	1
+ 760	9
+ 521	7
+ 126	8
+ 471	1
+ 532	3
+ 180	1
+ 668	4
+ 880	3
+ 961	0
+ 464	2
+ 450	10
+ 634	9
+ 685	9
+ 2	0
+ 809	10
+ 113	6
+ 826	6
+ 230	10
+ 405	7
+ 30	9
+ 14	2
+ 69	7
+ 563	9
+ 3	5
+ 978	5
+ 740	4
+ 420	4
+ 324	1
+ 252	3
+ 123	1
+ 283	2
+ 631	1
+ 871	9
+ 60	3
+ 561	1
+ 213	6
+ 301	3
+ 257	9
+ 232	3
+ 388	2
+ 727	1
+ 637	1
+ 501	10
+ 252	8
+ 288	4
+ 815	6
+ 612	4
+ 678	5
+ 306	7
+ 759	9
+ 829	10
+ 442	1
+ 255	7
+ 994	5
+ 959	4
+ 696	7
+ 509	3
+ 833	0
+ 294	1
+ 764	6
+ 461	6
+ 152	1
+ 25	8
+ 555	3
+ 569	3
+ 199	4
+ 287	6
+ 528	5
+ 339	5
+ 28	3
+ 903	7
+ 983	4
+ 57	8
+ 422	4
+ 902	2
+ 933	4
+ 765	1
+ 435	8
+ 915	10
+ 122	5
+ 304	3
+ 882	6
+ 961	4
+ 133	3
+ 931	2
+ 598	8
+ 885	6
+ 246	9
+ 397	7
+ 292	3
+ 853	2
+ 662	6
+ 310	1
+ 409	2
+ 86	5
+ 709	4
+ 852	6
+ 982	8
+ 1	1
+ 114	9
+ 276	7
+ 766	2
+ 293	0
+ 102	7
+ 680	4
+ 989	5
+ 620	7
+ 152	9
+ 747	6
+ 154	8
+ 92	9
+ 224	9
+ 454	2
+ 758	5
+ 321	9
+ 386	6
+ 584	2
+ 758	9
+ 164	9
+ 567	8
+ 255	6
+ 377	9
+ 207	5
+ 804	10
+ 89	10
+ 788	2
+ 821	0
+ 126	3
+ 218	9
+ 729	5
+ 757	1
+ 136	3
+ 267	9
+ 219	4
+ 755	8
+ 275	0
+ 342	7
+ 885	5
+ 179	7
+ 503	3
+ 648	3
+ 450	5
+ 303	6
+ 743	5
+ 460	5
+ 60	2
+ 587	2
+ 559	9
+ 91	8
+ 285	8
+ 563	6
+ 856	9
+ 211	7
+ 454	4
+ 430	10
+ 659	1
+ 249	1
+ 546	6
+ 685	3
+ 72	1
+ 762	1
+ 363	3
+ 328	9
+ 202	4
+ 699	5
+ 265	3
+ 47	1
+ 168	3
+ 862	6
+ 649	3
+ 580	3
+ 369	8
+ 417	9
+ 379	1
+ 205	5
+ 247	10
+ 583	6
+ 315	9
+ 532	5
+ 331	2
+ 5	6
+ 493	1
+ 717	7
+ 310	6
+ 283	10
+ 870	9
+ 267	2
+ 691	7
+ 154	1
+ 786	4
+ 522	0
+ 326	1
+ 642	6
+ 17	2
+ 158	3
+ 405	2
+ 943	9
+ 215	7
+ 559	5
+ 238	8
+ 484	1
+ 704	8
+ 346	4
+ 435	5
+ 465	2
+ 860	10
+ 253	2
+ 92	9
+ 826	1
+ 70	10
+ 456	5
+ 147	4
+ 373	4
+ 60	9
+ 887	3
+ 774	4
+ 405	5
+ 122	8
+ 873	6
+ 253	3
+ 778	1
+ 326	0
+ 298	4
+ 927	1
+ 527	10
+ 109	10
+ 471	3
+ 383	8
+ 618	4
+ 775	5
+ 740	5
+ 875	1
+ 27	10
+ 897	9
+ 554	1
+ 239	3
+ 263	6
+ 362	6
+ 982	3
+ 686	5
+ 285	8
+ 492	8
+ 51	9
+ 600	7
+ 317	4
+ 173	1
+ 924	0
+ 203	10
+ 45	1
+ 851	6
+ 250	1
+ 930	5
+ 654	3
+ 74	6
+ 581	8
+ 145	9
+ 554	6
+ 623	6
+ 511	2
+ 274	8
+ 598	4
+ 886	5
+ 496	1
+ 474	5
+ 189	3
+ 141	4
+ 414	1
+ 953	1
+ 363	0
+ 704	9
+ 786	8
+ 811	3
+ 485	4
+ 946	10
+ 657	2
+ 825	3
+ 668	7
+ 778	2
+ 800	3
+ 705	10
+ 576	8
+ 429	10
+ 916	4
+ 58	3
+ 409	8
+ 225	2
+ 610	0
+ 536	1
+ 470	5
+ 92	1
+ 702	9
+ 383	4
+ 628	2
+ 533	4
+ 412	2
+ 417	10
+ 84	8
+ 978	0
+ 229	0
+ 280	6
+ 798	5
+ 834	4
+ 540	4
+ 504	0
+ 852	6
+ 138	6
+ 512	5
+ 925	1
+ 682	5
+ 567	1
+ 696	10
+ 82	8
+ 830	1
+ 780	1
+ 96	1
+ 697	9
+ 565	5
+ 302	1
+ 900	8
+ 116	8
+ 401	3
+ 307	9
+ 774	2
+ 52	5
+ 690	6
+ 550	4
+ 603	6
+ 166	4
+ 691	9
+ 493	8
+ 7	2
+ 681	6
+ 720	10
+ 677	6
+ 789	8
+ 374	2
+ 46	7
+ 103	8
+ 913	2
+ 276	6
+ 774	8
+ 989	4
+ 457	2
+ 811	1
+ 102	3
+ 935	1
+ 493	6
+ 680	2
+ 601	4
+ 835	4
+ 149	2
+ 580	2
+ 889	7
+ 14	8
+ 838	3
+ 404	6
+ 115	4
+ 989	6
+ 548	8
+ 720	6
+ 103	7
+ 758	6
+ 272	4
+ 810	9
+ 795	6
+ 262	9
+ 852	8
+ 138	7
+ 525	2
+ 543	4
+ 442	9
+ 975	6
+ 340	10
+ 129	9
+ 764	8
+ 537	9
+ 504	3
+ 463	8
+ 733	3
+ 649	5
+ 916	9
+ 471	8
+ 754	6
+ 510	3
+ 761	1
+ 642	2
+ 1000	6
+ 761	3
+ 581	9
+ 227	3
+ 740	8
+ 211	2
+ 58	7
+ 21	8
+ 946	7
+ 318	9
+ 582	8
+ 631	3
+ 398	1
+ 615	2
+ 194	3
+ 363	2
+ 875	1
+ 533	5
+ 16	8
+ 801	8
+ 522	0
+ 0	6
+ 686	0
+ 371	6
+ 692	7
+ 494	3
+ 478	1
+ 610	9
+ 266	2
+ 36	5
+ 483	4
+ 653	4
+ 524	2
+ 814	5
+ 945	6
+ 296	5
+ 627	3
+ 47	3
+ 318	4
+ 944	0
+ 108	4
+ 283	6
+ 564	9
+ 463	8
+ 118	5
+ 290	6
+ 898	9
+ 959	4
+ 129	8
+ 963	1
+ 388	3
+ 541	0
+ 555	6
+ 327	9
+ 6	3
+ 882	1
+ 711	2
+ 700	3
+ 58	2
+ 105	2
+ 662	4
+ 777	6
+ 338	7
+ 983	5
+ 509	9
+ 540	9
+ 205	1
+ 912	8
+ 669	2
+ 633	7
+ 511	5
+ 790	2
+ 680	5
+ 496	7
+ 653	6
+ 915	3
+ 995	7
+ 875	3
+ 429	9
+ 800	9
+ 804	3
+ 835	0
+ 422	7
+ 768	1
+ 987	4
+ 767	5
+ 915	6
+ 720	6
+ 47	2
+ 334	7
+ 817	2
+ 15	8
+ 941	9
+ 145	4
+ 747	9
+ 307	6
+ 286	1
+ 559	7
+ 890	3
+ 798	9
+ 727	6
+ 375	6
+ 122	1
+ 238	2
+ 311	6
+ 869	1
+ 820	9
+ 941	8
+ 773	1
+ 130	5
+ 31	4
+ 70	3
+ 580	6
+ 24	5
+ 956	8
+ 347	7
+ 387	7
+ 325	5
+ 817	6
+ 678	1
+ 134	5
+ 257	10
+ 431	2
+ 715	2
+ 284	8
+ 724	3
+ 281	8
+ 632	9
+ 423	7
+ 331	4
+ 477	7
+ 62	9
+ 400	4
+ 374	2
+ 950	1
+ 346	1
+ 599	6
+ 38	0
+ 800	8
+ 234	1
+ 597	10
+ 399	9
+ 751	0
+ 740	2
+ 686	1
+ 554	2
+ 749	6
+ 28	1
+ 2	4
+ 366	10
+ 454	7
+ 36	1
+ 314	1
+ 83	1
+ 827	3
+ 198	4
+ 275	6
+ 304	0
+ 628	0
+ 201	3
+ 114	8
+ 477	9
+ 370	5
+ 12	4
+ 907	4
+ 324	4
+ 89	4
+ 414	4
+ 435	5
+ 517	3
+ 815	7
+ 687	1
+ 313	10
+ 116	9
+ 34	3
+ 255	1
+ 71	7
+ 12	4
+ 237	0
+ 812	1
+ 401	1
+ 505	5
+ 496	9
+ 893	9
+ 417	4
+ 193	2
+ 125	9
+ 321	4
+ 871	4
+ 380	9
+ 753	6
+ 53	8
+ 366	1
+ 264	6
+ 88	1
+ 747	5
+ 213	3
+ 979	7
+ 171	9
+ 640	6
+ 281	8
+ 819	4
+ 714	1
+ 845	6
+ 577	2
+ 489	3
+ 859	5
+ 154	2
+ 607	4
+ 828	7
+ 495	6
+ 184	7
+ 827	2
+ 417	10
+ 34	1
+ 586	3
+ 890	4
+ 721	6
+ 545	6
+ 188	1
+ 791	7
+ 452	7
+ 219	6
+ 875	8
+ 25	7
+ 521	5
+ 279	7
+ 228	1
+ 868	6
+ 105	9
+ 701	7
+ 217	6
+ 96	9
+ 196	6
+ 505	4
+ 763	3
+ 61	2
+ 946	3
+ 823	8
+ 107	8
+ 525	6
+ 368	8
+ 333	6
+ 910	2
+ 240	0
+ 103	9
+ 706	3
+ 533	8
+ 258	7
+ 443	8
+ 112	2
+ 58	2
+ 423	0
+ 455	2
+ 825	6
+ 93	4
+ 190	5
+ 154	5
+ 56	1
+ 725	3
+ 79	8
+ 237	8
+ 147	8
+ 587	4
+ 498	0
+ 167	6
+ 236	2
+ 785	7
+ 230	2
+ 904	1
+ 801	10
+ 405	10
+ 458	6
+ 515	5
+ 623	2
+ 810	7
+ 67	0
+ 486	2
+ 817	1
+ 619	3
+ 102	8
+ 926	3
+ 11	7
+ 998	2
+ 950	9
+ 297	8
+ 899	7
+ 743	4
+ 261	3
+ 871	9
+ 498	7
+ 585	6
+ 728	1
+ 779	5
+ 144	4
+ 861	2
+ 183	8
+ 585	2
+ 498	6
+ 436	4
+ 485	7
+ 200	4
+ 434	9
+ 741	7
+ 202	6
+ 578	7
+ 293	2
+ 264	0
+ 234	0
+ 566	4
+ 440	4
+ 624	6
+ 213	2
+ 817	7
+ 791	3
+ 160	3
+ 984	4
+ 660	4
+ 303	4
+ 113	5
+ 13	7
+ 204	3
+ 855	5
+ 326	1
+ 511	9
+ 467	10
+ 318	1
+ 573	5
+ 300	4
+ 242	1
+ 642	4
+ 367	6
+ 762	0
+ 45	1
+ 428	2
+ 570	4
+ 851	8
+ 746	7
+ 243	1
+ 795	8
+ 963	3
+ 705	3
+ 354	3
+ 811	7
+ 668	1
+ 744	3
+ 456	1
+ 937	2
+ 137	10
+ 283	6
+ 140	9
+ 5	10
+ 628	8
+ 697	9
+ 823	5
+ 626	8
+ 755	3
+ 66	1
+ 609	9
+ 762	3
+ 931	5
+ 586	4
+ 616	5
+ 604	8
+ 505	9
+ 318	6
+ 741	3
+ 636	4
+ 74	3
+ 241	9
+ 825	9
+ 683	6
+ 197	7
+ 688	8
+ 626	5
+ 82	6
+ 956	7
+ 944	6
+ 192	5
+ 325	7
+ 436	6
+ 342	2
+ 965	10
+ 547	0
+ 312	8
+ 936	1
+ 654	6
+ 717	9
+ 368	4
+ 658	10
+ 855	7
+ 551	8
+ 409	5
+ 382	6
+ 44	7
+ 298	5
+ 349	6
+ 658	3
+ 619	2
+ 354	9
+ 992	3
+ 67	6
+ 909	8
+ 498	3
+ 188	2
+ 271	0
+ 895	8
+ 854	3
+ 318	2
+ 905	4
+ 943	2
+ 843	3
+ 843	5
+ 607	5
+ 705	10
+ 392	7
+ 251	5
+ 343	2
+ 242	8
+ 437	4
+ 995	7
+ 474	9
+ 530	3
+ 195	8
+ 565	1
+ 210	5
+ 303	1
+ 800	1
+ 553	4
+ 609	3
+ 368	0
+ 955	6
+ 460	3
+ 780	7
+ 138	2
+ 133	1
+ 926	6
+ 24	5
+ 935	2
+ 304	5
+ 318	5
+ 8	6
+ 568	8
+ 768	1
+ 216	4
+ 379	6
+ 377	3
+ 204	8
+ 631	10
+ 539	8
+ 202	7
+ 901	1
+ 279	9
+ 584	2
+ 143	9
+ 714	5
+ 403	7
+ 83	10
+ 530	9
+ 92	7
+ 228	5
+ 331	6
+ 804	5
+ 442	4
+ 520	10
+ 203	7
+ 652	1
+ 850	9
+ 29	4
+ 145	2
+ 323	9
+ 633	7
+ 581	7
+ 696	1
+ 567	8
+ 857	8
+ 259	2
+ 400	1
+ 723	8
+ 498	2
+ 822	7
+ 965	5
+ 804	8
+ 405	8
+ 249	6
+ 5	6
+ 409	6
+ 297	10
+ 354	10
+ 100	9
+ 782	10
+ 716	0
+ 146	1
+ 104	9
+ 958	6
+ 112	8
+ 302	1
+ 255	1
+ 892	7
+ 939	1
+ 211	9
+ 713	6
+ 582	0
+ 609	9
+ 4	7
+ 857	8
+ 667	6
+ 828	8
+ 690	9
+ 683	6
+ 533	8
+ 428	8
+ 873	7
+ 942	8
+ 344	9
+ 907	6
+ 825	6
+ 174	4
+ 630	8
+ 343	6
+ 492	2
+ 421	2
+ 774	2
+ 972	5
+ 180	7
+ 112	7
+ 450	5
+ 549	3
+ 224	5
+ 88	6
+ 372	10
+ 122	2
+ 614	3
+ 604	2
+ 77	9
+ 880	6
+ 148	3
+ 728	9
+ 550	7
+ 386	7
+ 354	5
+ 444	8
+ 38	10
+ 127	3
+ 484	2
+ 829	9
+ 209	10
+ 53	8
+ 245	7
+ 68	3
+ 605	9
+ 892	8
+ 249	6
+ 674	8
+ 319	1
+ 529	7
+ 558	10
+ 478	6
+ 967	6
+ 858	5
+ 820	7
+ 307	0
+ 637	4
+ 852	9
+ 19	9
+ 205	6
+ 869	1
+ 376	1
+ 717	1
+ 917	0
+ 111	4
+ 709	7
+ 420	2
+ 265	4
+ 792	1
+ 838	6
+ 809	1
+ 640	4
+ 506	5
+ 328	5
+ 414	5
+ 148	3
+ 630	5
+ 400	3
+ 576	3
+ 382	7
+ 764	1
+ 356	2
+ 279	6
+ 571	1
+ 743	4
+ 683	6
+ 554	3
+ 998	1
+ 816	3
+ 585	2
+ 858	7
+ 512	5
+ 258	9
+ 835	8
+ 230	2
+ 520	10
+ 308	9
+ 177	6
+ 497	7
+ 659	2
+ 157	3
+ 793	7
+ 665	8
+ 772	5
+ 116	4
+ 711	10
+ 90	2
+ 463	3
+ 136	3
+ 181	4
+ 514	7
+ 359	8
+ 577	5
+ 410	1
+ 285	1
+ 314	4
+ 411	1
+ 153	1
+ 897	9
+ 557	0
+ 281	3
+ 988	4
+ 492	5
+ 719	6
+ 748	9
+ 993	3
+ 601	4
+ 85	2
+ 889	5
+ 251	2
+ 564	6
+ 616	10
+ 672	8
+ 50	6
+ 694	6
+ 582	10
+ 875	6
+ 346	4
+ 21	1
+ 994	8
+ 964	10
+ 31	6
+ 340	1
+ 742	2
+ 610	10
+ 402	2
+ 559	0
+ 148	2
+ 786	2
+ 800	5
+ 805	4
+ 455	7
+ 952	8
+ 48	10
+ 866	0
+ 741	8
+ 29	8
+ 395	4
+ 887	1
+ 597	5
+ 132	10
+ 670	7
+ 17	8
+ 921	8
+ 17	7
+ 283	8
+ 103	7
+ 503	1
+ 541	6
+ 27	4
+ 592	8
+ 238	6
+ 539	6
+ 990	4
+ 771	6
+ 922	9
+ 586	6
+ 593	6
+ 411	5
+ 406	4
+ 235	7
+ 250	3
+ 428	8
+ 393	10
+ 303	4
+ 376	9
+ 188	6
+ 516	7
+ 246	5
+ 153	0
+ 93	1
+ 919	7
+ 667	5
+ 282	1
+ 27	7
+ 506	3
+ 378	8
+ 600	8
+ 509	10
+ 775	8
+ 414	2
+ 707	6
+ 765	2
+ 328	0
+ 729	5
+ 28	8
+ 556	9
+ 501	2
+ 460	8
+ 301	5
+ 471	8
+ 749	8
+ 563	3
+ 655	1
+ 342	4
+ 883	8
+ 582	6
+ 357	3
+ 813	7
+ 357	5
+ 166	4
+ 364	7
+ 333	9
+ 945	8
+ 649	2
+ 280	1
+ 53	0
+ 969	6
+ 377	6
+ 688	7
+ 55	6
+ 476	6
+ 161	8
+ 983	10
+ 519	3
+ 516	7
+ 726	9
+ 407	1
+ 745	4
+ 853	4
+ 598	1
+ 514	7
+ 161	5
+ 268	5
+ 107	10
+ 258	2
+ 527	7
+ 799	7
+ 567	8
+ 663	1
+ 123	2
+ 772	8
+ 59	2
+ 909	8
+ 532	8
+ 197	1
+ 894	7
+ 781	1
+ 193	0
+ 593	3
+ 5	9
+ 463	5
+ 585	3
+ 221	2
+ 45	9
+ 238	2
+ 63	0
+ 18	1
+ 189	9
+ 925	7
+ 688	1
+ 851	6
+ 833	6
+ 636	0
+ 681	2
+ 327	7
+ 80	8
+ 217	7
+ 53	4
+ 817	1
+ 322	1
+ 266	4
+ 66	3
+ 506	3
+ 210	4
+ 976	9
+ 554	8
+ 480	4
+ 458	1
+ 414	1
+ 345	7
+ 824	4
+ 532	0
+ 90	6
+ 480	9
+ 683	8
+ 963	9
+ 187	0
+ 234	7
+ 284	4
+ 124	3
+ 342	7
+ 87	8
+ 66	5
+ 938	5
+ 683	3
+ 221	5
+ 708	8
+ 548	8
+ 338	0
+ 706	0
+ 830	7
+ 971	0
+ 699	2
+ 709	10
+ 649	8
+ 244	10
+ 511	3
+ 813	6
+ 875	8
+ 57	6
+ 34	3
+ 66	7
+ 30	6
+ 541	4
+ 642	2
+ 390	5
+ 917	4
+ 487	6
+ 565	2
+ 600	2
+ 29	8
+ 205	5
+ 174	0
+ 118	0
+ 769	2
+ 608	8
+ 452	7
+ 545	5
+ 288	1
+ 851	9
+ 333	2
+ 401	3
+ 601	9
+ 867	2
+ 84	5
+ 380	1
+ 311	6
+ 654	5
+ 604	8
+ 535	4
+ 947	1
+ 176	4
+ 817	7
+ 881	1
+ 808	7
+ 34	1
+ 972	4
+ 392	6
+ 322	3
+ 740	4
+ 725	1
+ 520	0
+ 706	2
+ 521	3
+ 947	1
+ 683	9
+ 199	9
+ 292	0
+ 581	2
+ 120	4
+ 905	2
+ 529	9
+ 588	9
+ 450	9
+ 179	2
+ 318	9
+ 310	8
+ 941	0
+ 13	5
+ 325	10
+ 518	0
+ 853	7
+ 867	1
+ 732	4
+ 318	9
+ 836	2
+ 6	4
+ 100	6
+ 287	6
+ 505	5
+ 741	8
+ 370	1
+ 661	3
+ 67	7
+ 773	4
+ 633	3
+ 401	5
+ 7	3
+ 631	7
+ 716	9
+ 592	6
+ 173	6
+ 917	3
+ 192	2
+ 824	7
+ 670	6
+ 520	0
+ 616	2
+ 351	7
+ 854	1
+ 75	5
+ 414	5
+ 973	4
+ 744	6
+ 160	5
+ 554	8
+ 11	7
+ 349	9
+ 0	5
+ 132	8
+ 239	8
+ 389	8
+ 842	0
+ 941	2
+ 688	8
+ 317	8
+ 282	7
+ 239	3
+ 151	10
+ 860	3
+ 441	4
+ 62	5
+ 141	4
+ 381	1
+ 952	5
+ 965	2
+ 315	4
+ 950	2
+ 359	9
+ 351	0
+ 686	7
+ 809	10
+ 397	0
+ 224	5
+ 30	1
+ 859	5
+ 497	9
+ 924	6
+ 331	3
+ 779	3
+ 818	7
+ 474	1
+ 99	4
+ 291	5
+ 316	6
+ 504	0
+ 308	3
+ 970	7
+ 361	2
+ 254	4
+ 277	1
+ 863	8
+ 33	8
+ 413	4
+ 93	2
+ 648	9
+ 937	1
+ 44	0
+ 546	3
+ 493	9
+ 976	10
+ 863	3
+ 310	8
+ 991	7
+ 27	2
+ 63	3
+ 358	9
+ 78	4
+ 714	5
+ 755	8
+ 682	4
+ 717	6
+ 525	8
+ 654	1
+ 97	1
+ 933	1
+ 144	8
+ 358	5
+ 629	3
+ 126	7
+ 593	2
+ 959	10
+ 115	0
+ 342	8
+ 527	1
+ 635	2
+ 501	4
+ 828	0
+ 114	5
+ 96	2
+ 630	0
+ 284	8
+ 825	6
+ 228	5
+ 990	4
+ 109	6
+ 542	1
+ 534	7
+ 105	9
+ 485	6
+ 974	1
+ 842	5
+ 473	7
+ 500	6
+ 152	6
+ 798	8
+ 625	1
+ 555	4
+ 723	8
+ 903	7
+ 136	0
+ 296	7
+ 80	8
+ 334	2
+ 706	8
+ 818	7
+ 940	7
+ 154	4
+ 329	7
+ 1000	5
+ 249	8
+ 264	9
+ 879	8
+ 323	6
+ 602	2
+ 314	7
+ 239	6
+ 416	3
+ 439	7
+ 505	1
+ 569	3
+ 825	5
+ 982	10
+ 921	3
+ 633	9
+ 793	9
+ 718	1
+ 756	6
+ 877	1
+ 198	5
+ 306	5
+ 217	5
+ 121	6
+ 864	6
+ 382	4
+ 706	10
+ 691	5
+ 460	7
+ 511	4
+ 985	1
+ 302	8
+ 26	0
+ 835	8
+ 617	7
+ 862	8
+ 191	2
+ 326	4
+ 713	4
+ 41	6
+ 8	4
+ 946	7
+ 375	6
+ 245	8
+ 310	8
+ 216	3
+ 900	5
+ 73	9
+ 538	9
+ 708	2
+ 620	6
+ 970	8
+ 738	3
+ 219	5
+ 743	3
+ 28	8
+ 683	10
+ 465	1
+ 611	7
+ 893	9
+ 466	1
+ 215	4
+ 626	3
+ 291	2
+ 196	10
+ 319	8
+ 569	3
+ 627	3
+ 585	8
+ 758	3
+ 107	8
+ 80	8
+ 759	5
+ 848	4
+ 255	7
+ 291	7
+ 849	5
+ 87	5
+ 794	4
+ 640	10
+ 378	10
+ 807	9
+ 249	4
+ 253	8
+ 281	0
+ 162	4
+ 797	2
+ 177	6
+ 787	0
+ 926	0
+ 766	2
+ 763	6
+ 723	9
+ 92	5
+ 228	7
+ 507	6
+ 692	3
+ 552	9
+ 748	8
+ 775	0
+ 817	9
+ 417	6
+ 179	6
+ 170	10
+ 619	1
+ 7	4
+ 312	8
+ 1	0
+ 621	1
+ 552	8
+ 825	1
+ 455	5
+ 373	0
+ 457	1
+ 813	2
+ 151	6
+ 169	6
+ 243	3
+ 161	4
+ 314	8
+ 508	3
+ 166	8
+ 92	2
+ 856	7
+ 259	4
+ 561	1
+ 467	0
+ 600	8
+ 24	1
+ 961	8
+ 289	1
+ 467	5
+ 679	7
+ 806	8
+ 124	1
+ 621	6
+ 441	8
+ 453	5
+ 954	3
+ 245	2
+ 716	8
+ 297	2
+ 823	9
+ 22	8
+ 955	10
+ 684	2
+ 95	2
+ 702	8
+ 862	5
+ 615	10
+ 628	2
+ 617	1
+ 23	1
+ 602	10
+ 378	8
+ 189	1
+ 654	5
+ 276	5
+ 383	3
+ 323	3
+ 281	0
+ 582	4
+ 159	3
+ 151	0
+ 793	8
+ 6	4
+ 2	6
+ 491	0
+ 693	1
+ 2	1
+ 941	2
+ 164	6
+ 677	4
+ 71	1
+ 737	4
+ 398	0
+ 402	10
+ 395	6
+ 264	5
+ 581	1
+ 312	6
+ 477	3
+ 209	10
+ 340	9
+ 61	3
+ 972	0
+ 532	1
+ 596	2
+ 576	7
+ 269	3
+ 61	7
+ 331	5
+ 647	7
+ 23	9
+ 272	6
+ 967	6
+ 190	4
+ 899	4
+ 413	2
+ 300	5
+ 581	3
+ 475	1
+ 408	1
+ 323	10
+ 738	6
+ 297	8
+ 259	6
+ 262	9
+ 354	3
+ 817	6
+ 890	8
+ 211	1
+ 230	1
+ 479	6
+ 350	8
+ 116	9
+ 52	6
+ 44	5
+ 662	4
+ 443	4
+ 959	7
+ 200	2
+ 368	5
+ 124	7
+ 748	9
+ 349	6
+ 727	6
+ 718	10
+ 671	2
+ 599	0
+ 977	7
+ 952	0
+ 307	10
+ 488	10
+ 363	9
+ 369	3
+ 672	6
+ 540	0
+ 31	7
+ 763	8
+ 606	1
+ 417	3
+ 672	1
+ 289	3
+ 333	9
+ 364	3
+ 604	3
+ 339	9
+ 311	8
+ 879	7
+ 759	2
+ 996	4
+ 817	5
+ 471	8
+ 199	2
+ 627	8
+ 346	0
+ 138	0
+ 180	4
+ 362	5
+ 316	7
+ 823	9
+ 41	2
+ 830	4
+ 989	7
+ 26	7
+ 957	0
+ 179	8
+ 557	7
+ 622	8
+ 885	2
+ 562	2
+ 294	7
+ 250	5
+ 128	6
+ 987	4
+ 337	8
+ 363	4
+ 972	2
+ 730	10
+ 901	8
+ 710	9
+ 777	9
+ 632	3
+ 540	3
+ 91	4
+ 503	7
+ 656	8
+ 353	9
+ 271	5
+ 517	3
+ 924	9
+ 68	3
+ 232	0
+ 480	10
+ 2	4
+ 717	7
+ 240	5
+ 600	9
+ 829	1
+ 126	9
+ 564	6
+ 573	2
+ 426	9
+ 126	7
+ 406	6
+ 955	3
+ 498	0
+ 618	7
+ 62	1
+ 692	1
+ 481	4
+ 775	7
+ 904	4
+ 592	7
+ 515	7
+ 652	1
+ 347	2
+ 300	8
+ 150	4
+ 471	6
+ 69	4
+ 887	6
+ 448	5
+ 297	5
+ 605	10
+ 574	1
+ 398	3
+ 806	3
+ 725	4
+ 34	2
+ 116	7
+ 320	5
+ 911	6
+ 237	1
+ 46	7
+ 619	1
+ 133	5
+ 682	6
+ 12	10
+ 91	6
+ 967	7
+ 702	4
+ 14	5
+ 667	7
+ 905	7
+ 978	0
+ 387	3
+ 484	3
+ 918	7
+ 361	10
+ 429	10
+ 78	6
+ 485	8
+ 143	5
+ 738	2
+ 113	7
+ 899	8
+ 70	9
+ 322	7
+ 651	2
+ 438	6
+ 247	8
+ 927	7
+ 124	8
+ 453	5
+ 808	9
+ 464	9
+ 445	9
+ 646	6
+ 446	4
+ 822	6
+ 89	7
+ 374	2
+ 633	7
+ 897	3
+ 923	3
+ 913	2
+ 160	8
+ 902	3
+ 684	4
+ 768	5
+ 237	2
+ 378	7
+ 181	0
+ 270	6
+ 408	1
+ 187	5
+ 814	6
+ 657	4
+ 257	6
+ 731	2
+ 889	6
+ 350	0
+ 484	3
+ 333	2
+ 607	1
+ 661	8
+ 333	0
+ 527	5
+ 63	8
+ 142	5
+ 890	3
+ 968	7
+ 889	6
+ 151	1
+ 179	9
+ 325	1
+ 526	7
+ 116	0
+ 927	4
+ 178	5
+ 550	8
+ 379	9
+ 877	9
+ 398	9
+ 703	5
+ 410	6
+ 868	4
+ 297	8
+ 3	4
+ 903	2
+ 329	2
+ 250	9
+ 903	4
+ 865	8
+ 815	0
+ 366	4
+ 881	7
+ 248	8
+ 651	6
+ 698	4
+ 185	1
+ 947	1
+ 487	2
+ 810	5
+ 691	7
+ 672	0
+ 940	9
+ 875	8
+ 287	7
diff --git a/contrib/bloom/expected/bloom.out b/contrib/bloom/expected/bloom.out
new file mode 100644
index ...f7334fa
*** a/contrib/bloom/expected/bloom.out
--- b/contrib/bloom/expected/bloom.out
***************
*** 0 ****
--- 1,120 ----
+ CREATE EXTENSION bloom;
+ CREATE TABLE tst (
+ 	i	int4,
+ 	t	text
+ );
+ \copy tst from 'data/data'
+ CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+ SET enable_seqscan=on;
+ SET enable_bitmapscan=off;
+ SET enable_indexscan=off;
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16;
+                 QUERY PLAN                 
+ -------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: (i = 16)
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: (i = 16)
+ (5 rows)
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE t = '5';
+                 QUERY PLAN                 
+ -------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: (t = '5'::text)
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: (t = '5'::text)
+ (5 rows)
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+                         QUERY PLAN                        
+ ----------------------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: ((i = 16) AND (t = '5'::text))
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: ((i = 16) AND (t = '5'::text))
+ (5 rows)
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ VACUUM ANALYZE tst;
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ VACUUM FULL tst;
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ RESET enable_seqscan;
+ RESET enable_bitmapscan;
+ RESET enable_indexscan;
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
new file mode 100644
index ...e5c7a86
*** a/contrib/bloom/sql/bloom.sql
--- b/contrib/bloom/sql/bloom.sql
***************
*** 0 ****
--- 1,46 ----
+ CREATE EXTENSION bloom;
+ 
+ CREATE TABLE tst (
+ 	i	int4,
+ 	t	text
+ );
+ 
+ \copy tst from 'data/data'
+ 
+ CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+ 
+ SET enable_seqscan=on;
+ SET enable_bitmapscan=off;
+ SET enable_indexscan=off;
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16;
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE t = '5';
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ VACUUM ANALYZE tst;
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ VACUUM FULL tst;
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ RESET enable_seqscan;
+ RESET enable_bitmapscan;
+ RESET enable_indexscan;
diff --git a/contrib/bloom/t/001_wal.pl b/contrib/bloom/t/001_wal.pl
new file mode 100644
index ...bbb398b
*** a/contrib/bloom/t/001_wal.pl
--- b/contrib/bloom/t/001_wal.pl
***************
*** 0 ****
--- 1,75 ----
+ # Test generic xlog record work for bloom index replication.
+ use strict;
+ use warnings;
+ use PostgresNode;
+ use TestLib;
+ use Test::More tests => 31;
+ 
+ my $node_master;
+ my $node_standby;
+ 
+ # Run few queries on both master and standby and check their results match.
+ sub test_index_replay
+ {
+ 	my ($test_name) = @_;
+ 
+ 	# Wait for standby to catch up
+ 	my $applname = $node_standby->name;
+ 	my $caughtup_query =
+ 		"SELECT pg_current_xlog_location() <= write_location FROM pg_stat_replication WHERE application_name = '$applname';";
+ 	$node_master->poll_query_until('postgres', $caughtup_query)
+ 	  or die "Timed out while waiting for standby 1 to catch up";
+ 
+ 	my $queries = qq(SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ SELECT * FROM tst WHERE i = 0;
+ SELECT * FROM tst WHERE i = 3;
+ SELECT * FROM tst WHERE t = 'b';
+ SELECT * FROM tst WHERE t = 'f';
+ SELECT * FROM tst WHERE i = 3 AND t = 'c';
+ SELECT * FROM tst WHERE i = 7 AND t = 'e';
+ );
+ 
+ 	# Run test queries and compare their result
+ 	my $master_result = $node_master->psql("postgres", $queries);
+ 	my $standby_result = $node_standby->psql("postgres", $queries);
+ 
+ 	is($master_result, $standby_result, "$test_name: query result matches");
+ }
+ 
+ # Initialize master node
+ $node_master = get_new_node('master');
+ $node_master->init(allows_streaming => 1);
+ $node_master->start;
+ my $backup_name = 'my_backup';
+ 
+ # Take backup
+ $node_master->backup($backup_name);
+ 
+ # Create streaming standby linking to master
+ $node_standby = get_new_node('standby');
+ $node_standby->init_from_backup($node_master, $backup_name,
+ 	has_streaming => 1);
+ $node_standby->start;
+ 
+ # Create some bloom index on master
+ $node_master->psql("postgres", "CREATE EXTENSION bloom;");
+ $node_master->psql("postgres", "CREATE TABLE tst (i int4, t text);");
+ $node_master->psql("postgres", "INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,100000) i;");
+ $node_master->psql("postgres", "CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);");
+ 
+ # Test that queries give same result
+ test_index_replay('initial');
+ 
+ # Run 10 cycles of table modification. Run test queries after each modification.
+ for (my $i = 1; $i <= 10; $i++)
+ {
+ 	$node_master->psql("postgres", "DELETE FROM tst WHERE i = $i;");
+ 	test_index_replay("delete $i");
+ 	$node_master->psql("postgres", "VACUUM tst;");
+ 	test_index_replay("vacuum $i");
+ 	my ($start, $end) = (100001 + ($i - 1) * 10000, 100000 + $i * 10000);
+ 	$node_master->psql("postgres", "INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series($start,$end) i;");
+ 	test_index_replay("insert $i");
+ }
diff --git a/doc/src/sgml/bloom.sgml b/doc/src/sgml/bloom.sgml
new file mode 100644
index ...c207e6d
*** a/doc/src/sgml/bloom.sgml
--- b/doc/src/sgml/bloom.sgml
***************
*** 0 ****
--- 1,218 ----
+ <!-- doc/src/sgml/bloom.sgml -->
+ 
+ <sect1 id="bloom" xreflabel="bloom">
+  <title>bloom</title>
+ 
+  <indexterm zone="bloom">
+   <primary>bloom</primary>
+  </indexterm>
+ 
+  <para>
+   <literal>bloom</> is a contrib which implements index access method.  It comes
+   as example of custom access methods and generic WAL records usage.  But it
+   is also useful itself.
+  </para>
+ 
+  <sect2>
+   <title>Introduction</title>
+ 
+   <para>
+    Implementation of
+    <ulink url="http://en.wikipedia.org/wiki/Bloom_filter">Bloom filter</ulink>
+    allows fast exclusion of non-candidate tuples.
+    Since signature is a lossy representation of all indexed attributes, 
+    search results should be rechecked using heap information. 
+    User can specify signature length (in uint16, default is 5) and the number of 
+    bits, which can be setted, per attribute (1 < colN < 2048).
+   </para>
+ 
+   <para>
+    This index is useful if table has many attributes and queries can include
+    their arbitary combinations.  Traditional <literal>btree</> index is faster
+    than bloom index, but it'd require too many indexes to support all possible 
+    queries, while one need only one bloom index.  Bloom index supports only 
+    equality comparison.  Since it's a signature file, not a tree, it always
+    should be readed fully, but sequentially, so index search performance is 
+    constant and doesn't depend on a query. 
+   </para>
+  </sect2>
+ 
+  <sect2>
+   <title>Parameters</title>
+ 
+   <para>
+    <literal>bloom</> indexes accept following parameters in <literal>WITH</>
+    clause.
+   </para>
+ 
+    <variablelist>
+    <varlistentry>
+     <term><literal>length</></term>
+     <listitem>
+      <para>
+       Length of signature in uint16 type values
+      </para>
+     </listitem>
+    </varlistentry>
+    </variablelist>
+    <variablelist>
+    <varlistentry>
+     <term><literal>col1 &mdash; col16</></term>
+     <listitem>
+      <para>
+       Number of bits for corresponding column
+      </para>
+     </listitem>
+    </varlistentry>
+    </variablelist>
+  </sect2>
+ 
+  <sect2>
+   <title>Examples</title>
+ 
+   <para>
+    Example of index definition is given below.
+   </para>
+ 
+ <programlisting>
+ CREATE INDEX bloomidx ON tbloom(i1,i2,i3) 
+        WITH (length=5, col1=2, col2=2, col3=4);
+ </programlisting>
+ 
+   <para>
+    Here, we create bloom index with signature length 80 bits and attributes
+    i1, i2  mapped to 2 bits, attribute i3 - to 4 bits.
+   </para>
+ 
+   <para>
+    Example of index definition and usage is given below.
+   </para>
+ 
+ <programlisting>
+ CREATE TABLE tbloom AS
+ SELECT
+     random()::int as i1,
+     random()::int as i2,
+     random()::int as i3,
+     random()::int as i4,
+     random()::int as i5,
+     random()::int as i6,
+     random()::int as i7,
+     random()::int as i8,
+     random()::int as i9,
+     random()::int as i10,
+     random()::int as i11,
+     random()::int as i12,
+     random()::int as i13
+ FROM
+     generate_series(1,1000);
+ CREATE INDEX bloomidx ON tbloom USING
+              bloom (i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12);
+ SELECT pg_relation_size('bloomidx');
+ CREATE index btree_idx ON tbloom(i1,i2,i3,i4,i5,i6,i7,i8,i9,i10,i11,i12);
+ SELECT pg_relation_size('btree_idx');
+ </programlisting>
+ 
+ <programlisting>
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                                    QUERY PLAN
+ -----------------------------------------------------------------------------------------------------------------
+  Bitmap Heap Scan on tbloom  (cost=1.50..5.52 rows=1 width=52) (actual time=0.057..0.057 rows=0 loops=1)
+    Recheck Cond: ((i2 = 20) AND (i10 = 15))
+    ->  Bitmap Index Scan on bloomidx  (cost=0.00..1.50 rows=1 width=0) (actual time=0.041..0.041 rows=9 loops=1)
+          Index Cond: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.081 ms
+ (5 rows)
+ </programlisting>
+ 
+   <para>
+    Seqscan is slow.
+   </para>
+ 
+ <programlisting>
+ =# SET enable_bitmapscan = off;
+ =# SET enable_indexscan = off;
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                             QUERY PLAN
+ --------------------------------------------------------------------------------------------------
+  Seq Scan on tbloom  (cost=0.00..25.00 rows=1 width=52) (actual time=0.162..0.162 rows=0 loops=1)
+    Filter: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.181 ms
+ (3 rows)
+ </programlisting>
+ 
+  <para>
+   Btree index will be not used for this query.
+  </para>
+ 
+ <programlisting>
+ =# DROP INDEX bloomidx;
+ =# CREATE INDEX btree_idx ON tbloom(i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12);
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                             QUERY PLAN
+ --------------------------------------------------------------------------------------------------
+  Seq Scan on tbloom (cost=0.00..25.00 rows=1 width=52) (actual time=0.210..0.210 rows=0 loops=1)
+    Filter: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.250 ms
+ (3 rows)
+ </programlisting>
+  </sect2>
+ 
+  <sect2>
+   <title>Opclass interface</title>
+ 
+   <para>
+    Bloom opclass interface is simple.  It requires 1 supporting function:
+    hash function for indexing datatype.  And it provides 1 search operator:
+    equality operator.  The example below shows <literal>opclass</> definition
+    for <literal>text</> datatype.
+   </para>
+ 
+ <programlisting>
+ CREATE OPERATOR CLASS text_ops
+ DEFAULT FOR TYPE text USING bloom AS
+     OPERATOR    1   =(text, text),
+     FUNCTION    1   hashtext(text);
+ </programlisting>
+  </sect2>
+ 
+  <sect2>
+   <title>Limitation</title>
+   <para>
+ 
+    <itemizedlist>
+     <listitem>
+      <para>
+       For now, only opclasses for <literal>int4</>, <literal>text</> comes
+       with contrib.  However, users may define more of them.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       Only <literal>=</literal> operator is supported for search now.  But it's
+       possible to add support of arrays with contains and intersection
+       operations in future.
+      </para>
+     </listitem>
+    </itemizedlist>
+   </para>
+  </sect2>
+ 
+  <sect2>
+   <title>Authors</title>
+ 
+   <para>
+    Teodor Sigaev <email>teodor@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+ 
+   <para>
+    Alexander Korotkov <email>a.korotkov@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+ 
+   <para>
+    Oleg Bartunov <email>obartunov@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+  </sect2>
+ 
+ </sect1>
diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
new file mode 100644
index 4e3f337..c8708ec
*** a/doc/src/sgml/contrib.sgml
--- b/doc/src/sgml/contrib.sgml
*************** CREATE EXTENSION <replaceable>module_nam
*** 105,110 ****
--- 105,111 ----
   &adminpack;
   &auth-delay;
   &auto-explain;
+  &bloom;
   &btree-gin;
   &btree-gist;
   &chkpass;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
new file mode 100644
index 30adece..7c73206
*** a/doc/src/sgml/filelist.sgml
--- b/doc/src/sgml/filelist.sgml
***************
*** 106,111 ****
--- 106,112 ----
  <!ENTITY adminpack       SYSTEM "adminpack.sgml">
  <!ENTITY auth-delay      SYSTEM "auth-delay.sgml">
  <!ENTITY auto-explain    SYSTEM "auto-explain.sgml">
+ <!ENTITY bloom           SYSTEM "bloom.sgml">
  <!ENTITY btree-gin       SYSTEM "btree-gin.sgml">
  <!ENTITY btree-gist      SYSTEM "btree-gist.sgml">
  <!ENTITY chkpass         SYSTEM "chkpass.sgml">
#56Oleg Bartunov
obartunov@gmail.com
In reply to: Alexander Korotkov (#55)
Re: WIP: Access method extendability

On Wed, Mar 9, 2016 at 8:31 PM, Alexander Korotkov <
a.korotkov@postgrespro.ru> wrote:

Hi!

On Wed, Mar 9, 2016 at 3:27 PM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:

Hi. As I just said to Tomas Vondra: since your patch creates a new
object type, please make sure to add a case to cover it in the
object_address.sql test. That verifies some things such as
pg_identify_object and related.

Good catch, thanks! Tests were added.
I also introduced numbering into patch names to make evident the order to
their application.

Nice to see progress ! I hope to see Alexander' work in 9.6.

I and Teodor will show at PGCon new GIN AM as an extension, optimized for
full text search (millisecond FTS) , which we gave up to push into core.

Show quoted text

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#57Teodor Sigaev
teodor@sigaev.ru
In reply to: Alexander Korotkov (#55)
Re: WIP: Access method extendability

Good catch, thanks! Tests were added.

I don't see any objection, is consensus reached? I'm close to comiit that...

--
Teodor Sigaev E-mail: teodor@sigaev.ru
WWW: http://www.sigaev.ru/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#58Petr Jelinek
petr@2ndquadrant.com
In reply to: Teodor Sigaev (#57)
Re: WIP: Access method extendability

On 16/03/16 15:31, Teodor Sigaev wrote:

Good catch, thanks! Tests were added.

I don't see any objection, is consensus reached? I'm close to comiit
that...

I did only cursory review on the bloom contrib module and don't really
have complaints there, but I know you can review that one. I'd like the
English of the generic_xlog.c description improved but I won't get to it
before weekend.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#59Teodor Sigaev
teodor@sigaev.ru
In reply to: Petr Jelinek (#58)
Re: WIP: Access method extendability

I don't see any objection, is consensus reached? I'm close to comiit
that...

I did only cursory review on the bloom contrib module and don't really have
complaints there, but I know you can review that one. I'd like the English of
the generic_xlog.c description improved but I won't get to it before weekend.

So, per patch status:
create-am
ready
generic-xlog
need to fix comments/docs
bloom-contrib
need review. I'm an original author of this module and of course
I can do some review of it but, seems, it's needed more eyes.
--
Teodor Sigaev E-mail: teodor@sigaev.ru
WWW: http://www.sigaev.ru/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#60Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Teodor Sigaev (#59)
Re: WIP: Access method extendability

Teodor Sigaev wrote:

I don't see any objection, is consensus reached? I'm close to comiit
that...

I did only cursory review on the bloom contrib module and don't really have
complaints there, but I know you can review that one. I'd like the English of
the generic_xlog.c description improved but I won't get to it before weekend.

So, per patch status:
create-am
ready

Please wait a bit on this one -- I think more review is warranted.
I will try to review it early next week.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#61Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Alexander Korotkov (#40)
Re: WIP: Access method extendability

Alexander Korotkov wrote:

Hi!

Thank you for review!

So. Before this version of the patch was posted in Nov 4th 2015, both
Tom and Heikki had said essentially "CREATE ACCESS METHOD is worthless,
let's pursue this stuff without those commands".
/messages/by-id/54730DFD.2060703@vmware.com (Nov 2014)
/messages/by-id/26822.1414516012@sss.pgh.pa.us (Oct 2014)

And then Alexander posted this version, without any discussion that
evidenced that those old objections were overridden. What happened
here? Did somebody discuss this in unarchived meetings? If so, it
would be good to know about that in this thread.

I noticed this state of affairs because I started reading the complete
thread in order to verify whether a consensus had been reached regarding
the move of IndexQualInfo and GenericCosts to selfuncs.h. But I only
see that Jim Nasby mentioned it and Alexander said "yes they move";
nothing else. The reason for raising this is that, according to
Alexander, new index AMs will want to use those structs; but current
index AMs only include index_selfuncs.h, and none of them includes
selfuncs.h, so it seems completely wrong to be importing all the planner
knowledge embodied in selfuncs.h into extension index AMs.

One observation is that the bloom AM patch (0003 of this series) uses
GenericCosts but not IndexQualInfo. But btcostestimate() and
gincostestimate() both use IndexQualInfo, so other AMs might well want
to use it too.

We could move GenericCosts and the prototype for genericcostestimate()
to index_selfuncs.h; that much is pretty reasonable. But I'm not sure
what to do about IndexQualInfo. We could just add forward struct
declarations for RestrictInfo and Node to index_selfuncs.h. But then
the extension code is going to have to import the includes were those
are defined anyway. Certainly it seems that deconstruct_indexquals()
(which returns a list of IndexQualInfo) is going to be needed by
extension index AMs, at least ...

I've made a few edits to the patch already, but I need to do some more
testing. Particularly I would like to understand the relcache issues to
figure out whether the current one is right.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#62Petr Jelinek
petr@2ndquadrant.com
In reply to: Alvaro Herrera (#61)
Re: WIP: Access method extendability

On 21/03/16 23:26, Alvaro Herrera wrote:

Alexander Korotkov wrote:

Hi!

Thank you for review!

So. Before this version of the patch was posted in Nov 4th 2015, both
Tom and Heikki had said essentially "CREATE ACCESS METHOD is worthless,
let's pursue this stuff without those commands".
/messages/by-id/54730DFD.2060703@vmware.com (Nov 2014)
/messages/by-id/26822.1414516012@sss.pgh.pa.us (Oct 2014)

And in sequence am thread Robert said the opposite.

And then Alexander posted this version, without any discussion that
evidenced that those old objections were overridden. What happened
here? Did somebody discuss this in unarchived meetings? If so, it
would be good to know about that in this thread.

Well there are two main reasons for having DDL, one is dependency
tracking and the other is binary upgrade. We can solve part of
dependency tracking by recoding dependency between opclass and amhandler
instead of opclass and access method, that would work fine. I don't know
how to clean pg_am on DROP EXTENSION though without the dependency support.

I also am not sure what is good way of solving binary upgrade without
any kind of DDL. Adding another pg_catalog.binary_upgrade_<something>
function would be potential solution if we really think DDL is bad idea
for access methods in general. Actually thinking of this, we might
actually need function like in any case if we are recoding dependencies
on access methods (which means it would probably be better to record
dependency of opclass on amhandler as mentioned before, since this is
already solved for functions and if the oid of am is not referenced
anywhere it does not need special handling for binary upgrade).

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#63Alexander Korotkov
aekorotkov@gmail.com
In reply to: Alvaro Herrera (#61)
Re: WIP: Access method extendability

On Tue, Mar 22, 2016 at 1:26 AM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:

So. Before this version of the patch was posted in Nov 4th 2015, both
Tom and Heikki had said essentially "CREATE ACCESS METHOD is worthless,
let's pursue this stuff without those commands".
/messages/by-id/54730DFD.2060703@vmware.com (Nov
2014)

Yes, that's it. In this email Heikki asserts that INSERTs into pg_am tables
in extensions would survive pg_upgrade, because pg_dump will dump CREATE
EXTENSION command which execute extension script.
In my response I noticed that it's not correct. pg_dump in binary upgrade
mode works so that we will miss records in pg_am. I haven't any answer
here.
/messages/by-id/CAPpHfdsbkNtLchypNGR0Ffu9wLHh__NukDp13qLqUKgTNV_N4A@mail.gmail.com
And, as Petr Jelinek mentioned, there is also problem of dependencies in
DROP EXTENSION.

/messages/by-id/26822.1414516012@sss.pgh.pa.us (Oct
2014)

And then Alexander posted this version, without any discussion that
evidenced that those old objections were overridden. What happened
here? Did somebody discuss this in unarchived meetings? If so, it
would be good to know about that in this thread.

After that Tom Lane committed very huge patch about rework AM API. One of
aims of this patch was support for CREATE ACCESS METHOD command.
http://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=65c5fcd353a859da9e61bfb2b92a99f12937de3b
I've initially positioned this patch as refactoring for support CREATE
ACCESS METHOD command.
/messages/by-id/CAPpHfdtLiSXmXk2b4tW+4+No_1-T0raO5fOYszhO6+Sn2Om+xw@mail.gmail.com
During discussing Tom mentioned future CREATE ACCESS METHOD command.
/messages/by-id/16164.1439222900@sss.pgh.pa.us
I don't think Tom would put so much efforts in this direction if he would
remain opposed to this command.

I noticed this state of affairs because I started reading the complete

thread in order to verify whether a consensus had been reached regarding
the move of IndexQualInfo and GenericCosts to selfuncs.h. But I only
see that Jim Nasby mentioned it and Alexander said "yes they move";
nothing else. The reason for raising this is that, according to
Alexander, new index AMs will want to use those structs; but current
index AMs only include index_selfuncs.h, and none of them includes
selfuncs.h, so it seems completely wrong to be importing all the planner
knowledge embodied in selfuncs.h into extension index AMs.

One observation is that the bloom AM patch (0003 of this series) uses
GenericCosts but not IndexQualInfo. But btcostestimate() and
gincostestimate() both use IndexQualInfo, so other AMs might well want
to use it too.

We could move GenericCosts and the prototype for genericcostestimate()
to index_selfuncs.h; that much is pretty reasonable. But I'm not sure
what to do about IndexQualInfo. We could just add forward struct
declarations for RestrictInfo and Node to index_selfuncs.h. But then
the extension code is going to have to import the includes were those
are defined anyway. Certainly it seems that deconstruct_indexquals()
(which returns a list of IndexQualInfo) is going to be needed by
extension index AMs, at least ...

Thank you for highlighting these issues.

I've made a few edits to the patch already, but I need to do some more
testing.

Did you already fix the issues above or do you need me to fix them?

Particularly I would like to understand the relcache issues to
figure out whether the current one is right.

Good point. I'll also try to find relcache issues.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#64Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Alexander Korotkov (#63)
Re: WIP: Access method extendability

Alexander Korotkov wrote:

On Tue, Mar 22, 2016 at 1:26 AM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:

I noticed this state of affairs because I started reading the complete
thread in order to verify whether a consensus had been reached regarding
the move of IndexQualInfo and GenericCosts to selfuncs.h. But I only
see that Jim Nasby mentioned it and Alexander said "yes they move";
nothing else. The reason for raising this is that, according to
Alexander, new index AMs will want to use those structs; but current
index AMs only include index_selfuncs.h, and none of them includes
selfuncs.h, so it seems completely wrong to be importing all the planner
knowledge embodied in selfuncs.h into extension index AMs.

One observation is that the bloom AM patch (0003 of this series) uses
GenericCosts but not IndexQualInfo. But btcostestimate() and
gincostestimate() both use IndexQualInfo, so other AMs might well want
to use it too.

We could move GenericCosts and the prototype for genericcostestimate()
to index_selfuncs.h; that much is pretty reasonable. But I'm not sure
what to do about IndexQualInfo. We could just add forward struct
declarations for RestrictInfo and Node to index_selfuncs.h. But then
the extension code is going to have to import the includes were those
are defined anyway. Certainly it seems that deconstruct_indexquals()
(which returns a list of IndexQualInfo) is going to be needed by
extension index AMs, at least ...

Thank you for highlighting these issues.

After thinking some more about this, I think it's all right to move both
IndexQualInfo and GenericCosts to selfuncs.h. The separation that we
want is this: the selectivity functions themselves need access to a lot
of planner knowledge, and so selfuncs.h knows a lot about planner. But
the code that _calls_ the selectivity function doesn't; and
index_selfuncs.h is about the latter. Properly architected extension
index AMs are going to have their selectivity functions in a separate .c
file which knows a lot about planner, and which includes selfuncs.h (and
maybe index_selfuncs.h if it wants to piggyback on the existing
selecticity functions, but that's unlikely); only the prototype for the
selfunc is going to be needed elsewhere, and the rest of the index code
is not going to know anything about planner stuff so it will not need to
include selfuncs.h nor index_selfuncs.h.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#65Robert Haas
robertmhaas@gmail.com
In reply to: Alexander Korotkov (#63)
Re: WIP: Access method extendability

On Tue, Mar 22, 2016 at 5:50 AM, Alexander Korotkov
<aekorotkov@gmail.com> wrote:

And then Alexander posted this version, without any discussion that
evidenced that those old objections were overridden. What happened
here? Did somebody discuss this in unarchived meetings? If so, it
would be good to know about that in this thread.

After that Tom Lane committed very huge patch about rework AM API. One of
aims of this patch was support for CREATE ACCESS METHOD command.
http://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=65c5fcd353a859da9e61bfb2b92a99f12937de3b
I've initially positioned this patch as refactoring for support CREATE
ACCESS METHOD command.
/messages/by-id/CAPpHfdtLiSXmXk2b4tW+4+No_1-T0raO5fOYszhO6+Sn2Om+xw@mail.gmail.com
During discussing Tom mentioned future CREATE ACCESS METHOD command.
/messages/by-id/16164.1439222900@sss.pgh.pa.us
I don't think Tom would put so much efforts in this direction if he would
remain opposed to this command.

Yeah, I rather thought we had consensus on that.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#66Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Robert Haas (#65)
Re: WIP: Access method extendability

Robert Haas wrote:

On Tue, Mar 22, 2016 at 5:50 AM, Alexander Korotkov
<aekorotkov@gmail.com> wrote:

And then Alexander posted this version, without any discussion that
evidenced that those old objections were overridden. What happened
here? Did somebody discuss this in unarchived meetings? If so, it
would be good to know about that in this thread.

After that Tom Lane committed very huge patch about rework AM API. One of
aims of this patch was support for CREATE ACCESS METHOD command.
http://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=65c5fcd353a859da9e61bfb2b92a99f12937de3b
I've initially positioned this patch as refactoring for support CREATE
ACCESS METHOD command.
/messages/by-id/CAPpHfdtLiSXmXk2b4tW+4+No_1-T0raO5fOYszhO6+Sn2Om+xw@mail.gmail.com
During discussing Tom mentioned future CREATE ACCESS METHOD command.
/messages/by-id/16164.1439222900@sss.pgh.pa.us
I don't think Tom would put so much efforts in this direction if he would
remain opposed to this command.

Yeah, I rather thought we had consensus on that.

I vaguely remembered so too, but was startled to see that this thread
didn't have any evidence of it -- I thought I was misremembering. I'm
glad we're all in the same page.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#67Alexander Korotkov
aekorotkov@gmail.com
In reply to: Alvaro Herrera (#64)
Re: WIP: Access method extendability

On Tue, Mar 22, 2016 at 11:53 PM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:

Alexander Korotkov wrote:

On Tue, Mar 22, 2016 at 1:26 AM, Alvaro Herrera <

alvherre@2ndquadrant.com>

wrote:

I noticed this state of affairs because I started reading the complete
thread in order to verify whether a consensus had been reached

regarding

the move of IndexQualInfo and GenericCosts to selfuncs.h. But I only
see that Jim Nasby mentioned it and Alexander said "yes they move";
nothing else. The reason for raising this is that, according to
Alexander, new index AMs will want to use those structs; but current
index AMs only include index_selfuncs.h, and none of them includes
selfuncs.h, so it seems completely wrong to be importing all the

planner

knowledge embodied in selfuncs.h into extension index AMs.

One observation is that the bloom AM patch (0003 of this series) uses
GenericCosts but not IndexQualInfo. But btcostestimate() and
gincostestimate() both use IndexQualInfo, so other AMs might well want
to use it too.

We could move GenericCosts and the prototype for genericcostestimate()
to index_selfuncs.h; that much is pretty reasonable. But I'm not sure
what to do about IndexQualInfo. We could just add forward struct
declarations for RestrictInfo and Node to index_selfuncs.h. But then
the extension code is going to have to import the includes were those
are defined anyway. Certainly it seems that deconstruct_indexquals()
(which returns a list of IndexQualInfo) is going to be needed by
extension index AMs, at least ...

Thank you for highlighting these issues.

After thinking some more about this, I think it's all right to move both
IndexQualInfo and GenericCosts to selfuncs.h. The separation that we
want is this: the selectivity functions themselves need access to a lot
of planner knowledge, and so selfuncs.h knows a lot about planner. But
the code that _calls_ the selectivity function doesn't; and
index_selfuncs.h is about the latter. Properly architected extension
index AMs are going to have their selectivity functions in a separate .c
file which knows a lot about planner, and which includes selfuncs.h (and
maybe index_selfuncs.h if it wants to piggyback on the existing
selecticity functions, but that's unlikely); only the prototype for the
selfunc is going to be needed elsewhere, and the rest of the index code
is not going to know anything about planner stuff so it will not need to
include selfuncs.h nor index_selfuncs.h.

Right, the purposes of headers are:
* selfuncs.h – for those who going to estimate selectivity,
* index_selfuncs.h – for those who going to call selectivity estimation
functions of built-in AMs.

From this point for view we should keep both IndexQualInfo and GenericCosts
in selfuncs.h.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#68Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Alexander Korotkov (#55)
Re: WIP: Access method extendability

I don't quite see how this is supposed to work:

+ #ifdef WAL_DEBUG
+   /*
+    * If xlog debug is enabled then check produced delta.  Result of delta
+    * application to saved image should be the same as current page state.
+    */
+   if (XLOG_DEBUG)
+   {
+       char    tmp[BLCKSZ];
+       memcpy(tmp, image, BLCKSZ);
+       applyPageRedo(tmp, pageData->data, pageData->dataLen);
+       elog(ERROR, "result of generic xlog apply doesn't match");
+   }
+ #endif

I suppose the elog(ERROR) call should be conditional ...

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#69Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Teodor Sigaev (#59)
Re: WIP: Access method extendability

Teodor Sigaev wrote:

So, per patch status:
create-am
ready

Teodor agreed to me committing this one instead of him; thanks. I just
pushed it after some mostly cosmetic adjustments.

I pass the baton back to Teodor for the remaining patches in this
series.

Thanks,

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#70Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Alvaro Herrera (#68)
2 attachment(s)
Re: WIP: Access method extendability

Hi!

Thank you for committing CREATE ACCESS METHOD command!

On Thu, Mar 24, 2016 at 4:06 AM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:

I don't quite see how this is supposed to work:

+ #ifdef WAL_DEBUG
+   /*
+    * If xlog debug is enabled then check produced delta.  Result of delta
+    * application to saved image should be the same as current page state.
+    */
+   if (XLOG_DEBUG)
+   {
+       char    tmp[BLCKSZ];
+       memcpy(tmp, image, BLCKSZ);
+       applyPageRedo(tmp, pageData->data, pageData->dataLen);
+       elog(ERROR, "result of generic xlog apply doesn't match");
+   }
+ #endif

I suppose the elog(ERROR) call should be conditional ...

Good catch. Check condition was lost between versions.
Attached patches are rebased to master. Now, it checks that page images
match except area between pd_lower and pd_upper. I've tested it with WAL
debug and it works.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

Attachments:

0002-generic-xlog.12.patchapplication/octet-stream; name=0002-generic-xlog.12.patchDownload
diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
new file mode 100644
index c72a1f2..c0e38fd
*** a/src/backend/access/rmgrdesc/Makefile
--- b/src/backend/access/rmgrdesc/Makefile
*************** subdir = src/backend/access/rmgrdesc
*** 8,16 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o gindesc.o gistdesc.o \
! 	   hashdesc.o heapdesc.o mxactdesc.o nbtdesc.o relmapdesc.o \
! 	   replorigindesc.o seqdesc.o smgrdesc.o spgdesc.o \
  	   standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
  
  include $(top_srcdir)/src/backend/common.mk
--- 8,16 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o genericdesc.o \
! 	   gindesc.o gistdesc.o hashdesc.o heapdesc.o mxactdesc.o nbtdesc.o \
! 	   relmapdesc.o replorigindesc.o seqdesc.o smgrdesc.o spgdesc.o \
  	   standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
  
  include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/genericdesc.c b/src/backend/access/rmgrdesc/genericdesc.c
new file mode 100644
index ...3d035c2
*** a/src/backend/access/rmgrdesc/genericdesc.c
--- b/src/backend/access/rmgrdesc/genericdesc.c
***************
*** 0 ****
--- 1,58 ----
+ /*-------------------------------------------------------------------------
+  *
+  * genericdesc.c
+  *	  rmgr descriptor routines for access/transam/generic_xlog.c
+  *
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/backend/access/rmgrdesc/genericdesc.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/generic_xlog.h"
+ #include "lib/stringinfo.h"
+ #include "storage/relfilenode.h"
+ 
+ /*
+  * Description of generic xlog record: write page regions which this record
+  * overrides.
+  */
+ void
+ generic_desc(StringInfo buf, XLogReaderState *record)
+ {
+ 	Pointer		ptr = XLogRecGetData(record),
+ 				end = ptr + XLogRecGetDataLen(record);
+ 
+ 	while (ptr < end)
+ 	{
+ 		OffsetNumber	offset,
+ 						length;
+ 
+ 		memcpy(&offset, ptr, sizeof(offset));
+ 		ptr += sizeof(offset);
+ 		memcpy(&length, ptr, sizeof(length));
+ 		ptr += sizeof(length);
+ 		ptr += length;
+ 
+ 		if (ptr < end)
+ 			appendStringInfo(buf, "offset %u, length %u; ", offset, length);
+ 		else
+ 			appendStringInfo(buf, "offset %u, length %u", offset, length);
+ 	}
+ 
+ 	return;
+ }
+ 
+ /*
+  * Identification of generic xlog record: we don't distinguish any subtypes
+  * inside generic xlog records.
+  */
+ const char *
+ generic_identify(uint8 info)
+ {
+ 	return "Generic";
+ }
diff --git a/src/backend/access/transam/Makefile b/src/backend/access/transam/Makefile
new file mode 100644
index 94455b2..16fbe47
*** a/src/backend/access/transam/Makefile
--- b/src/backend/access/transam/Makefile
*************** subdir = src/backend/access/transam
*** 12,19 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = clog.o commit_ts.o multixact.o parallel.o rmgr.o slru.o subtrans.o \
! 	timeline.o transam.o twophase.o twophase_rmgr.o varsup.o \
  	xact.o xlog.o xlogarchive.o xlogfuncs.o \
  	xloginsert.o xlogreader.o xlogutils.o
  
--- 12,19 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = clog.o commit_ts.o generic_xlog.o multixact.o parallel.o rmgr.o slru.o \
! 	subtrans.o timeline.o transam.o twophase.o twophase_rmgr.o varsup.o \
  	xact.o xlog.o xlogarchive.o xlogfuncs.o \
  	xloginsert.o xlogreader.o xlogutils.o
  
diff --git a/src/backend/access/transam/generic_xlog.c b/src/backend/access/transam/generic_xlog.c
new file mode 100644
index ...7ca03bf
*** a/src/backend/access/transam/generic_xlog.c
--- b/src/backend/access/transam/generic_xlog.c
***************
*** 0 ****
--- 1,510 ----
+ /*-------------------------------------------------------------------------
+  *
+  * generic_xlog.c
+  *	 Implementation of generic xlog records.
+  *
+  *
+  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/backend/access/transam/generic_xlog.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/generic_xlog.h"
+ #include "access/xlogutils.h"
+ #include "miscadmin.h"
+ #include "utils/memutils.h"
+ 
+ /*-------------------------------------------------------------------------
+  * API for construction of generic xlog records
+  *
+  * This API allows user to construct generic xlog records which are
+  * describing difference between pages in general way.  Thus it's useful
+  * for extension which provides custom access methods because they couldn't
+  * register their own WAL redo routines.
+  *
+  * Generic xlog record should be constructed in following steps.
+  * 1) GenericXLogStart(relation) - start construction of generic xlog
+  *	  record for given relation.
+  * 2) GenericXLogRegister(buffer, isNew) - register one or more buffers
+  *	  for generic xlog record.  This function return a copy of page image
+  *	  where modifications should be performed.  The second argument
+  *	  indicates that block is new and full image should be taken.
+  * 3) Do modification of page images obtained in previous step.
+  * 4) GenericXLogFinish() - finish construction of generic xlog record.
+  *
+  * Please, note following points while constructing generic xlog records.
+  * - No direct modifications of page images are allowed! All modifications
+  *	 should be done in copies returned by GenericXLogRegister().  Literally
+  *	 code which makes generic xlog records should never call
+  *	 BufferGetPage() function.
+  * - On any step generic xlog record construction could be canceled by
+  *	 calling GenericXLogAbort().  All changes made in page images copies
+  *	 would be discarded.
+  * - Registrations of buffers (step 2) and modifications of page images
+  *	 (step 3) could be mixed in any sequence.  The only restriction is that
+  *	 you can modify page image only after registration of corresponding
+  *	 buffer.
+  * - After registration buffer also can be unregistered by calling
+  *	 GenericXLogUnregister(buffer).  In this case changes made in particular
+  *	 page image copy will be discarded.
+  * - Generic xlog assumes that pages are using standard layout.  I.e. all
+  *	 information between pd_lower and pd_upper will be discarded.
+  * - Maximum number of buffers simultaneously registered for generic xlog
+  *	 is MAX_GENERIC_XLOG_PAGES.  Error would be thrown if this limit
+  *	 exceeded.
+  * - Since you modify copies of page images, GenericXLogStart() doesn't
+  *	 start a critical section.  Thus, you can do memory allocation, error
+  *	 throwing etc between GenericXLogStart() and GenericXLogFinish().
+  *	 Actual critical section present inside GenericXLogFinish().
+  * - GenericXLogFinish() takes care about marking buffers dirty and setting
+  *	 their LSNs.  You don't need to do this explicitly.
+  * - For unlogged relations, everything work the same expect there is no
+  *	 WAL record produced.  Thus, you typically don't need to do any explicit
+  *	 checks for unlogged relations.
+  * - If registered buffer isn't new, generic xlog record contains delta
+  *	 between old and new page images.  This delta is produced by per byte
+  *	 comparison.  Current delta mechanist is not effective for data shift
+  *	 inside the page.  However, it could be improved in further versions.
+  * - Generic xlog redo function will acquire exclusive locks to buffers
+  *	 in the same order they were registered.  After redo of all changes
+  *	 locks would be released in the same order.  That could makes sense for
+  *	 concurrency.
+  *
+  * Internally delta between pages consists of set of fragments.  Each fragment
+  * represents changes made in given region of page.  Fragment is described
+  * as following.
+  *
+  * - offset of page region (OffsetNumber)
+  * - length of page region (OffsetNumber)
+  * - data - the data to place into described region ('length' number of bytes)
+  *
+  * Unchanged regions of page are uncovered by these fragments.  This is why
+  * delta could be more compact than full page image.  But if unchanged region
+  * of page is less than fragment header (offset and length) then it would
+  * increase size of delta instead of decreasing.  Thus, we break fragment only
+  * for unchanged regions greater than MATCH_THRESHOLD.
+  *
+  * The worst case for delta size is when we didn't find any unchanged region
+  * in the page. Then size of delta would be size of page plus size of fragment
+  * header.
+  */
+ #define FRAGMENT_HEADER_SIZE	(2 * sizeof(OffsetNumber))
+ #define MATCH_THRESHOLD			FRAGMENT_HEADER_SIZE
+ #define MAX_DELTA_SIZE			BLCKSZ + FRAGMENT_HEADER_SIZE
+ 
+ /* Struct of generic xlog data for single page */
+ typedef struct
+ {
+ 	Buffer	buffer;			/* registered buffer */
+ 	char	image[BLCKSZ];	/* copy of page image for modification */
+ 	char	data[MAX_DELTA_SIZE]; /* delta between page images */
+ 	int		dataLen;		/* space consumed in data field */
+ 	bool	fullImage;		/* are we taking full image of this page? */
+ } PageData;
+ 
+ /* Enum of generic xlog (gxlog) status */
+ enum GenericXlogStatus
+ {
+ 	GXLOG_NOT_STARTED,	/* gxlog is not started */
+ 	GXLOG_LOGGED,		/* gxlog is started for logged relation */
+ 	GXLOG_UNLOGGED		/* gxlog is started for unlogged relation */
+ };
+ 
+ static enum GenericXlogStatus	genericXlogStatus = GXLOG_NOT_STARTED;
+ static PageData					pages[MAX_GENERIC_XLOG_PAGES];
+ 
+ 
+ static void writeFragment(PageData *pageData, OffsetNumber offset,
+ 						  OffsetNumber len, Pointer data);
+ static void writeDelta(PageData *pageData);
+ static void applyPageRedo(Page page, Pointer data, Size dataSize);
+ 
+ /*
+  * Write next fragment into delta.
+  */
+ static void
+ writeFragment(PageData *pageData, OffsetNumber offset, OffsetNumber length,
+ 			  Pointer data)
+ {
+ 	Pointer			ptr = pageData->data + pageData->dataLen;
+ 
+ 	/* Check we have enough of space */
+ 	Assert(pageData->dataLen + sizeof(offset) +
+ 		   sizeof(length) + length <= sizeof(pageData->data));
+ 
+ 	/* Write fragment data */
+ 	memcpy(ptr, &offset, sizeof(offset));
+ 	ptr += sizeof(offset);
+ 	memcpy(ptr, &length, sizeof(length));
+ 	ptr += sizeof(length);
+ 	memcpy(ptr, data, length);
+ 	ptr += length;
+ 
+ 	pageData->dataLen = ptr - pageData->data;
+ }
+ 
+ /*
+  * Make delta for given page.
+  */
+ static void
+ writeDelta(PageData *pageData)
+ {
+ 	Page			page = BufferGetPage(pageData->buffer),
+ 					image = (Page) pageData->image;
+ 	int				i,
+ 					fragmentBegin = -1,
+ 					fragmentEnd = -1;
+ 	uint16			pageLower = ((PageHeader) page)->pd_lower,
+ 					pageUpper = ((PageHeader) page)->pd_upper,
+ 					imageLower = ((PageHeader) image)->pd_lower,
+ 					imageUpper = ((PageHeader) image)->pd_upper;
+ 
+ 	for (i = 0; i < BLCKSZ; i++)
+ 	{
+ 		bool	match;
+ 
+ 		/*
+ 		 * Check if bytes in old and new page images matches.  We don't rely
+ 		 * data in unallocated area between pd_lower and pd_upper.  Thus we
+ 		 * assume unallocated area to expand with unmatched bytes.  Bytes
+ 		 * inside unallocated area are assumed to always match.
+ 		 */
+ 		if (i < pageLower)
+ 		{
+ 			if (i < imageLower)
+ 				match = (page[i] == image[i]);
+ 			else
+ 				match = false;
+ 		}
+ 		else if (i >= pageUpper)
+ 		{
+ 			if (i >= imageUpper)
+ 				match = (page[i] == image[i]);
+ 			else
+ 				match = false;
+ 		}
+ 		else
+ 		{
+ 			match = true;
+ 		}
+ 
+ 		if (match)
+ 		{
+ 			if (fragmentBegin >= 0)
+ 			{
+ 				/* Matched byte is potential of fragment. */
+ 				if (fragmentEnd < 0)
+ 					fragmentEnd = i;
+ 
+ 				/*
+ 				 * Write next fragment if sequence of matched bytes is longer
+ 				 * than MATCH_THRESHOLD.
+ 				 */
+ 				if (i - fragmentEnd >= MATCH_THRESHOLD)
+ 				{
+ 					writeFragment(pageData, fragmentBegin,
+ 								  fragmentEnd - fragmentBegin,
+ 								  page + fragmentBegin);
+ 					fragmentBegin = -1;
+ 					fragmentEnd = -1;
+ 				}
+ 			}
+ 		}
+ 		else
+ 		{
+ 			/* On unmatched byte, start new fragment if it's not done yet */
+ 			if (fragmentBegin < 0)
+ 				fragmentBegin = i;
+ 			fragmentEnd = -1;
+ 		}
+ 	}
+ 
+ 	if (fragmentBegin >= 0)
+ 		writeFragment(pageData, fragmentBegin,
+ 					  BLCKSZ - fragmentBegin,
+ 					  page + fragmentBegin);
+ 
+ #ifdef WAL_DEBUG
+ 	/*
+ 	 * If xlog debug is enabled then check produced delta.  Result of delta
+ 	 * application to saved image should be the same as current page state.
+ 	 */
+ 	if (XLOG_DEBUG)
+ 	{
+ 		char	tmp[BLCKSZ];
+ 		memcpy(tmp, image, BLCKSZ);
+ 		applyPageRedo(tmp, pageData->data, pageData->dataLen);
+ 		if (memcmp(tmp, page, pageLower)
+ 			|| memcmp(tmp + pageUpper, page + pageUpper, BLCKSZ - pageUpper))
+ 			elog(ERROR, "result of generic xlog apply doesn't match");
+ 	}
+ #endif
+ }
+ 
+ /*
+  * Start new generic xlog record.
+  */
+ void
+ GenericXLogStart(Relation relation)
+ {
+ 	int i;
+ 
+ 	if (genericXlogStatus != GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogStart: generic xlog is already started")));
+ 
+ 	genericXlogStatus = RelationNeedsWAL(relation) ? GXLOG_LOGGED : GXLOG_UNLOGGED;
+ 
+ 	for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 	{
+ 		pages[i].buffer = InvalidBuffer;
+ 	}
+ }
+ 
+ /*
+  * Register new buffer for generic xlog record.
+  */
+ Page
+ GenericXLogRegister(Buffer buffer, bool isNew)
+ {
+ 	int block_id;
+ 
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogRegister: generic xlog isn't started")));
+ 
+ 	/* Place new buffer to unused slot in array */
+ 	for (block_id = 0; block_id < MAX_GENERIC_XLOG_PAGES; block_id++)
+ 	{
+ 		if (BufferIsInvalid(pages[block_id].buffer))
+ 		{
+ 			pages[block_id].buffer = buffer;
+ 			memcpy(pages[block_id].image, BufferGetPage(buffer), BLCKSZ);
+ 			pages[block_id].dataLen = 0;
+ 			pages[block_id].fullImage = isNew;
+ 			return (Page)pages[block_id].image;
+ 		}
+ 		else if (pages[block_id].buffer == buffer)
+ 		{
+ 			/* 
+ 			 * Buffer already registered.  Just return image which is already
+ 			 * prepared.
+ 			 */
+ 			return (Page)pages[block_id].image;
+ 		}
+ 	}
+ 
+ 	ereport(ERROR,
+ 			(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ 			 errmsg("GenericXLogRegister: maximum number of %d buffers is exceeded",
+ 					MAX_GENERIC_XLOG_PAGES)));
+ 
+ 	/* keep compiler quiet */
+ 	return NULL;
+ }
+ 
+ /*
+  * Unregister particular buffer for generic xlog record.
+  */
+ void
+ GenericXLogUnregister(Buffer buffer)
+ {
+ 	int block_id;
+ 
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogUnregister: generic xlog isn't started")));
+ 
+ 	/* Find block in array to unregister */
+ 	for (block_id = 0; block_id < MAX_GENERIC_XLOG_PAGES; block_id++)
+ 	{
+ 		if (pages[block_id].buffer == buffer)
+ 		{
+ 			/*
+ 			 * Preserve order of pages in array because it could matter for
+ 			 * concurrency.
+ 			 */
+ 			memmove(&pages[block_id], &pages[block_id + 1],
+ 					(MAX_GENERIC_XLOG_PAGES - block_id - 1) * sizeof(PageData));
+ 			pages[MAX_GENERIC_XLOG_PAGES - 1].buffer = InvalidBuffer;
+ 			return;
+ 		}
+ 	}
+ 
+ 	ereport(ERROR,
+ 			(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 			 errmsg("GenericXLogUnregister: registered buffer not found")));
+ }
+ 
+ /*
+  * Put all changes in registered buffers to generic xlog record.
+  */
+ XLogRecPtr
+ GenericXLogFinish(void)
+ {
+ 	XLogRecPtr lsn = InvalidXLogRecPtr;
+ 	int i;
+ 
+ 	if (genericXlogStatus == GXLOG_LOGGED)
+ 	{
+ 		/* Logged relation: make xlog record in critical section. */
+ 		START_CRIT_SECTION();
+ 		XLogBeginInsert();
+ 
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			char	tmp[BLCKSZ];
+ 
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 
+ 			/* Swap current and saved page image. */
+ 			memcpy(tmp, pages[i].image, BLCKSZ);
+ 			memcpy(pages[i].image, BufferGetPage(pages[i].buffer), BLCKSZ);
+ 			memcpy(BufferGetPage(pages[i].buffer), tmp, BLCKSZ);
+ 
+ 			if (pages[i].fullImage)
+ 			{
+ 				/* Full page image doesn't require anything special from us */
+ 				XLogRegisterBuffer(i, pages[i].buffer, REGBUF_FORCE_IMAGE);
+ 			}
+ 			else
+ 			{
+ 				/*
+ 				 * In normal node calculate delta and write use it as data
+ 				 * associated with this page.
+ 				 */
+ 				XLogRegisterBuffer(i, pages[i].buffer, REGBUF_STANDARD);
+ 				writeDelta(&pages[i]);
+ 				XLogRegisterBufData(i, pages[i].data, pages[i].dataLen);
+ 			}
+ 		}
+ 
+ 		/* Insert xlog record */
+ 		lsn = XLogInsert(RM_GENERIC_ID, 0);
+ 
+ 		/* Set LSN and make buffers dirty */
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 			PageSetLSN(BufferGetPage(pages[i].buffer), lsn);
+ 			MarkBufferDirty(pages[i].buffer);
+ 		}
+ 		END_CRIT_SECTION();
+ 	}
+ 	else if (genericXlogStatus == GXLOG_UNLOGGED)
+ 	{
+ 		/* Unlogged relation: skip xlog-related stuff */
+ 		START_CRIT_SECTION();
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 			memcpy(BufferGetPage(pages[i].buffer), pages[i].image, BLCKSZ);
+ 			MarkBufferDirty(pages[i].buffer);
+ 		}
+ 		END_CRIT_SECTION();
+ 	}
+ 	else
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogFinish: generic xlog isn't started")));
+ 	}
+ 
+ 	genericXlogStatus = GXLOG_NOT_STARTED;
+ 
+ 	return lsn;
+ }
+ 
+ /*
+  * Abort generic xlog record.
+  */
+ void
+ GenericXLogAbort(void)
+ {
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogAbort: generic xlog isn't started")));
+ 
+ 	genericXlogStatus = GXLOG_NOT_STARTED;
+ }
+ 
+ /*
+  * Apply delta to given page image.
+  */
+ static void
+ applyPageRedo(Page page, Pointer data, Size dataSize)
+ {
+ 	Pointer ptr = data, end = data + dataSize;
+ 
+ 	while (ptr < end)
+ 	{
+ 		OffsetNumber	offset,
+ 						length;
+ 
+ 		memcpy(&offset, ptr, sizeof(offset));
+ 		ptr += sizeof(offset);
+ 		memcpy(&length, ptr, sizeof(length));
+ 		ptr += sizeof(length);
+ 
+ 		memcpy(page + offset, ptr, length);
+ 
+ 		ptr += length;
+ 	}
+ }
+ 
+ /*
+  * Redo function for generic xlog record.
+  */
+ void
+ generic_redo(XLogReaderState *record)
+ {
+ 	uint8		block_id;
+ 	Buffer		buffers[MAX_GENERIC_XLOG_PAGES] = {InvalidBuffer};
+ 	XLogRecPtr	lsn = record->EndRecPtr;
+ 
+ 	Assert(record->max_block_id < MAX_GENERIC_XLOG_PAGES);
+ 
+ 	/* Interate over blocks */
+ 	for (block_id = 0; block_id <= record->max_block_id; block_id++)
+ 	{
+ 		XLogRedoAction action;
+ 
+ 		if (!XLogRecHasBlockRef(record, block_id))
+ 			continue;
+ 
+ 		action = XLogReadBufferForRedo(record, block_id, &buffers[block_id]);
+ 
+ 		/* Apply redo to given block if needed */
+ 		if (action == BLK_NEEDS_REDO)
+ 		{
+ 			Pointer	blockData;
+ 			Size	blockDataSize;
+ 			Page	page;
+ 
+ 			page = BufferGetPage(buffers[block_id]);
+ 			blockData = XLogRecGetBlockData(record, block_id, &blockDataSize);
+ 			applyPageRedo(page, blockData, blockDataSize);
+ 
+ 			PageSetLSN(page, lsn);
+ 			MarkBufferDirty(buffers[block_id]);
+ 		}
+ 	}
+ 
+ 	/* Changes are done: unlock and release all buffers */
+ 	for (block_id = 0; block_id <= record->max_block_id; block_id++)
+ 	{
+ 		if (BufferIsValid(buffers[block_id]))
+ 			UnlockReleaseBuffer(buffers[block_id]);
+ 	}
+ }
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
new file mode 100644
index 7c4d773..7b38c16
*** a/src/backend/access/transam/rmgr.c
--- b/src/backend/access/transam/rmgr.c
***************
*** 11,16 ****
--- 11,17 ----
  #include "access/commit_ts.h"
  #include "access/gin.h"
  #include "access/gist_private.h"
+ #include "access/generic_xlog.h"
  #include "access/hash.h"
  #include "access/heapam_xlog.h"
  #include "access/brin_xlog.h"
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
new file mode 100644
index 13af485..262deb2
*** a/src/backend/replication/logical/decode.c
--- b/src/backend/replication/logical/decode.c
*************** LogicalDecodingProcessRecord(LogicalDeco
*** 143,148 ****
--- 143,149 ----
  		case RM_BRIN_ID:
  		case RM_COMMIT_TS_ID:
  		case RM_REPLORIGIN_ID:
+ 		case RM_GENERIC_ID:
  			/* just deal with xid, and done */
  			ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(record),
  									buf.origptr);
diff --git a/src/bin/pg_xlogdump/.gitignore b/src/bin/pg_xlogdump/.gitignore
new file mode 100644
index eebaf30..33a1acf
*** a/src/bin/pg_xlogdump/.gitignore
--- b/src/bin/pg_xlogdump/.gitignore
***************
*** 4,9 ****
--- 4,10 ----
  /clogdesc.c
  /committsdesc.c
  /dbasedesc.c
+ /genericdesc.c
  /gindesc.c
  /gistdesc.c
  /hashdesc.c
diff --git a/src/bin/pg_xlogdump/rmgrdesc.c b/src/bin/pg_xlogdump/rmgrdesc.c
new file mode 100644
index f9cd395..cff7e59
*** a/src/bin/pg_xlogdump/rmgrdesc.c
--- b/src/bin/pg_xlogdump/rmgrdesc.c
***************
*** 11,16 ****
--- 11,17 ----
  #include "access/brin_xlog.h"
  #include "access/clog.h"
  #include "access/commit_ts.h"
+ #include "access/generic_xlog.h"
  #include "access/gin.h"
  #include "access/gist_private.h"
  #include "access/hash.h"
diff --git a/src/include/access/generic_xlog.h b/src/include/access/generic_xlog.h
new file mode 100644
index ...49249e0
*** a/src/include/access/generic_xlog.h
--- b/src/include/access/generic_xlog.h
***************
*** 0 ****
--- 1,36 ----
+ /*-------------------------------------------------------------------------
+  *
+  * generic_xlog.h
+  *	  Generic xlog API definition.
+  *
+  *
+  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/access/generic_xlog.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef GENERIC_XLOG_H
+ #define GENERIC_XLOG_H
+ 
+ #include "access/xlog.h"
+ #include "access/xlog_internal.h"
+ #include "storage/bufpage.h"
+ #include "utils/rel.h"
+ 
+ #define MAX_GENERIC_XLOG_PAGES	  3
+ 
+ /* API for construction of generic xlog records */
+ extern void GenericXLogStart(Relation relation);
+ extern Page GenericXLogRegister(Buffer buffer, bool isNew);
+ extern void GenericXLogUnregister(Buffer buffer);
+ extern XLogRecPtr GenericXLogFinish(void);
+ extern void GenericXLogAbort(void);
+ 
+ /* functions defined for rmgr */
+ extern void generic_redo(XLogReaderState *record);
+ extern const char *generic_identify(uint8 info);
+ extern void generic_desc(StringInfo buf, XLogReaderState *record);
+ 
+ #endif   /* GENERIC_XLOG_H */
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
new file mode 100644
index fab912d..3cfe6f7
*** a/src/include/access/rmgrlist.h
--- b/src/include/access/rmgrlist.h
*************** PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo
*** 45,47 ****
--- 45,48 ----
  PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL)
  PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL)
  PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL)
+ PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL)
0003-bloom-contrib.12.patchapplication/octet-stream; name=0003-bloom-contrib.12.patchDownload
diff --git a/contrib/Makefile b/contrib/Makefile
new file mode 100644
index d12dd63..25263c0
*** a/contrib/Makefile
--- b/contrib/Makefile
*************** SUBDIRS = \
*** 8,13 ****
--- 8,14 ----
  		adminpack	\
  		auth_delay	\
  		auto_explain	\
+ 		bloom		\
  		btree_gin	\
  		btree_gist	\
  		chkpass		\
diff --git a/contrib/bloom/.gitignore b/contrib/bloom/.gitignore
new file mode 100644
index ...5dcb3ff
*** a/contrib/bloom/.gitignore
--- b/contrib/bloom/.gitignore
***************
*** 0 ****
--- 1,4 ----
+ # Generated subdirectories
+ /log/
+ /results/
+ /tmp_check/
diff --git a/contrib/bloom/Makefile b/contrib/bloom/Makefile
new file mode 100644
index ...13bd397
*** a/contrib/bloom/Makefile
--- b/contrib/bloom/Makefile
***************
*** 0 ****
--- 1,24 ----
+ # contrib/bloom/Makefile
+ 
+ MODULE_big = bloom
+ OBJS = blcost.o blinsert.o blscan.o blutils.o blvacuum.o blvalidate.o $(WIN32RES)
+ 
+ EXTENSION = bloom
+ DATA = bloom--1.0.sql
+ PGFILEDESC = "bloom access method - signature file based index"
+ 
+ REGRESS = bloom
+ 
+ ifdef USE_PGXS
+ PG_CONFIG = pg_config
+ PGXS := $(shell $(PG_CONFIG) --pgxs)
+ include $(PGXS)
+ else
+ subdir = contrib/bloom
+ top_builddir = ../..
+ include $(top_builddir)/src/Makefile.global
+ include $(top_srcdir)/contrib/contrib-global.mk
+ endif
+ 
+ wal-check: temp-install
+ 	$(prove_check)
diff --git a/contrib/bloom/blcost.c b/contrib/bloom/blcost.c
new file mode 100644
index ...d918dce
*** a/contrib/bloom/blcost.c
--- b/contrib/bloom/blcost.c
***************
*** 0 ****
--- 1,45 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blcost.c
+  *		Cost estimate function for bloom indexes.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blcost.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "fmgr.h"
+ #include "optimizer/cost.h"
+ #include "utils/selfuncs.h"
+ 
+ #include "bloom.h"
+ 
+ void
+ blcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
+ 			   Cost *indexStartupCost, Cost *indexTotalCost,
+ 			   Selectivity *indexSelectivity, double *indexCorrelation)
+ {
+ 	IndexOptInfo   *index = path->indexinfo;
+ 	List		   *qinfos;
+ 	GenericCosts	costs;
+ 
+ 	/* Do preliminary analysis of indexquals */
+ 	qinfos = deconstruct_indexquals(path);
+ 
+ 	MemSet(&costs, 0, sizeof(costs));
+ 
+ 	/* We have to visit all index tuples anyway */
+ 	costs.numIndexTuples = index->tuples;
+ 
+ 	/* Use generic estimate */
+ 	genericcostestimate(root, path, loop_count, qinfos, &costs);
+ 
+ 	*indexStartupCost = costs.indexStartupCost;
+ 	*indexTotalCost = costs.indexTotalCost;
+ 	*indexSelectivity = costs.indexSelectivity;
+ 	*indexCorrelation = costs.indexCorrelation;
+ }
diff --git a/contrib/bloom/blinsert.c b/contrib/bloom/blinsert.c
new file mode 100644
index ...15bff40
*** a/contrib/bloom/blinsert.c
--- b/contrib/bloom/blinsert.c
***************
*** 0 ****
--- 1,293 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blinsert.c
+  *		Bloom index build and insert functions.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blinsert.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "access/generic_xlog.h"
+ #include "catalog/index.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ 
+ #include "bloom.h"
+ 
+ PG_MODULE_MAGIC;
+ 
+ typedef struct
+ {
+ 	BloomState		blstate;
+ 	MemoryContext	tmpCtx;
+ 	char			data[BLCKSZ];
+ 	int64			count;
+ } BloomBuildState;
+ 
+ /*
+  * Flush page cached in BloomBuildState.
+  */
+ static void
+ flushCachedPage(Relation index, BloomBuildState *buildstate)
+ {
+ 	Page	page;
+ 	Buffer	buffer = BloomNewBuffer(index);
+ 
+ 	GenericXLogStart(index);
+ 	page = GenericXLogRegister(buffer, true);
+ 	memcpy(page, buildstate->data, BLCKSZ);
+ 	GenericXLogFinish();
+ 	UnlockReleaseBuffer(buffer);
+ }
+ 
+ /*
+  * (Re)initialize cached page in BloomBuildState.
+  */
+ static void
+ initCachedPage(BloomBuildState *buildstate)
+ {
+ 	memset(buildstate->data, 0, BLCKSZ);
+ 	BloomInitPage(buildstate->data, 0);
+ 	buildstate->count = 0;
+ }
+ 
+ /*
+  * Per-tuple callback from IndexBuildHeapScan.
+  */
+ static void
+ bloomBuildCallback(Relation index, HeapTuple htup, Datum *values,
+ 					bool *isnull, bool tupleIsAlive, void *state)
+ {
+ 	BloomBuildState	*buildstate = (BloomBuildState *)state;
+ 	MemoryContext	 oldCtx;
+ 	BloomTuple		*itup;
+ 
+ 	oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
+ 
+ 	itup = BloomFormTuple(&buildstate->blstate, &htup->t_self, values, isnull);
+ 
+ 	/* Try to add next item to cached page */
+ 	if (BloomPageAddItem(&buildstate->blstate, buildstate->data, itup) == false)
+ 	{
+ 		/* Cached page is full, flush it out and make a new one */
+ 		flushCachedPage(index, buildstate);
+ 
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		initCachedPage(buildstate);
+ 
+ 		if (BloomPageAddItem(&buildstate->blstate, buildstate->data, itup) == false)
+ 		{
+ 			/* We shouldn't be here since we're inserting to the empty page */
+ 			elog(ERROR, "can not add new tuple");
+ 		}
+ 	}
+ 	else
+ 	{
+ 		buildstate->count++;
+ 	}
+ 
+ 	MemoryContextSwitchTo(oldCtx);
+ 	MemoryContextReset(buildstate->tmpCtx);
+ }
+ 
+ IndexBuildResult *
+ blbuild(Relation heap, Relation index, IndexInfo *indexInfo)
+ {
+ 	IndexBuildResult   *result;
+ 	double				reltuples;
+ 	BloomBuildState		buildstate;
+ 
+ 	if (RelationGetNumberOfBlocks(index) != 0)
+ 		elog(ERROR, "index \"%s\" already contains data",
+ 			RelationGetRelationName(index));
+ 
+ 	/* Initialize the meta page */
+ 	BloomInitMetapage(index);
+ 
+ 	initBloomState(&buildstate.blstate, index);
+ 
+ 	buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
+ 												"Bloom build temporary context",
+ 												ALLOCSET_DEFAULT_MINSIZE,
+ 												ALLOCSET_DEFAULT_INITSIZE,
+ 												ALLOCSET_DEFAULT_MAXSIZE);
+ 
+ 	initCachedPage(&buildstate);
+ 
+ 	/* Do the heap scan */
+ 	reltuples = IndexBuildHeapScan(heap, index, indexInfo, true,
+ 									bloomBuildCallback, (void *) &buildstate);
+ 
+ 	if (buildstate.count > 0)
+ 		flushCachedPage(index, &buildstate);
+ 
+ 	MemoryContextDelete(buildstate.tmpCtx);
+ 
+ 	result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
+ 	result->heap_tuples = result->index_tuples = reltuples;
+ 
+ 	return result;
+ }
+ 
+ void
+ blbuildempty(Relation index)
+ {
+ 	if (RelationGetNumberOfBlocks(index) != 0)
+ 		elog(ERROR, "index \"%s\" already contains data",
+ 			RelationGetRelationName(index));
+ 
+ 	/* Initialize the meta page */
+ 	BloomInitMetapage(index);
+ }
+ 
+ bool
+ blinsert(Relation index, Datum *values, bool *isnull,
+ 		ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique)
+ {
+ 	BloomState			blstate;
+ 	BloomTuple		   *itup;
+ 	MemoryContext		oldCtx;
+ 	MemoryContext		insertCtx;
+ 	BloomMetaPageData  *metaData;
+ 	Buffer				buffer,
+ 						metaBuffer;
+ 	Page				page,
+ 						metaPage;
+ 	BlockNumber			blkno = InvalidBlockNumber;
+ 	OffsetNumber		nStart;
+ 
+ 	insertCtx = AllocSetContextCreate(CurrentMemoryContext,
+ 										"Bloom insert temporary context",
+ 										ALLOCSET_DEFAULT_MINSIZE,
+ 										ALLOCSET_DEFAULT_INITSIZE,
+ 										ALLOCSET_DEFAULT_MAXSIZE);
+ 
+ 	oldCtx = MemoryContextSwitchTo(insertCtx);
+ 
+ 	initBloomState(&blstate, index);
+ 	itup = BloomFormTuple(&blstate, ht_ctid, values, isnull);
+ 
+ 	/*
+ 	 * At first, try to insert new tuple to the first page in
+ 	 * notFullPage array.  If success we don't need to modify the
+ 	 * meta page.
+ 	 */
+ 	metaBuffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 	LockBuffer(metaBuffer, BUFFER_LOCK_SHARE);
+ 	metaData = BloomPageGetMeta(BufferGetPage(metaBuffer));
+ 
+ 	if (metaData->nEnd > metaData->nStart)
+ 	{
+ 		Page	page;
+ 
+ 		blkno = metaData->notFullPage[metaData->nStart];
+ 
+ 		Assert(blkno != InvalidBlockNumber);
+ 		LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		GenericXLogStart(index);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		if (BloomPageAddItem(&blstate, page, itup))
+ 		{
+ 			GenericXLogFinish();
+ 			UnlockReleaseBuffer(buffer);
+ 			ReleaseBuffer(metaBuffer);
+ 			goto away;
+ 		}
+ 		else
+ 		{
+ 			GenericXLogAbort();
+ 			UnlockReleaseBuffer(buffer);
+ 		}
+ 	}
+ 	else
+ 	{
+ 		/* First page in notFullPage isn't suitable */
+ 		LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
+ 	}
+ 
+ 	/*
+ 	 * Try other pages in notFullPage array.  We will have to change nStart
+ 	 * in metapage.  Thus, grab exclusive lock on metapage.
+ 	 */
+ 	LockBuffer(metaBuffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 	GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(metaBuffer, false);
+ 	metaData = BloomPageGetMeta(metaPage);
+ 
+ 	/*
+ 	 * Iterate over notFullPage array.  Skip page we already tried first.
+ 	 */
+ 	nStart = metaData->nStart;
+ 	if (metaData->nEnd > nStart &&
+ 		blkno == metaData->notFullPage[nStart] )
+ 		nStart++;
+ 
+ 	while (metaData->nEnd > nStart)
+ 	{
+ 		blkno = metaData->notFullPage[nStart];
+ 		Assert(blkno != InvalidBlockNumber);
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		if (BloomPageAddItem(&blstate, page, itup))
+ 		{
+ 			metaData->nStart = nStart;
+ 			GenericXLogFinish();
+ 			UnlockReleaseBuffer(buffer);
+ 			UnlockReleaseBuffer(metaBuffer);
+ 			goto away;
+ 		}
+ 		else
+ 		{
+ 			GenericXLogUnregister(buffer);
+ 			UnlockReleaseBuffer(buffer);
+ 		}
+ 		nStart++;
+ 	}
+ 
+ 	GenericXLogAbort();
+ 
+ 	/*
+ 	 * Didn't find place to insert in notFullPage array.  Allocate new page.
+ 	 */
+ 	buffer = BloomNewBuffer(index);
+ 
+ 	GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(metaBuffer, false);
+ 	metaData = BloomPageGetMeta(metaPage);
+ 	page = GenericXLogRegister(buffer, true);
+ 	BloomInitPage(page, 0);
+ 	BloomPageAddItem(&blstate, page, itup);
+ 
+ 	metaData->nStart = 0;
+ 	metaData->nEnd = 1;
+ 	metaData->notFullPage[ 0 ] = BufferGetBlockNumber(buffer);
+ 
+ 	GenericXLogFinish();
+ 
+ 	UnlockReleaseBuffer(buffer);
+ 	UnlockReleaseBuffer(metaBuffer);
+ 
+ away:
+ 	MemoryContextSwitchTo(oldCtx);
+ 	MemoryContextDelete(insertCtx);
+ 
+ 	return false;
+ }
diff --git a/contrib/bloom/bloom--1.0.sql b/contrib/bloom/bloom--1.0.sql
new file mode 100644
index ...7fa7513
*** a/contrib/bloom/bloom--1.0.sql
--- b/contrib/bloom/bloom--1.0.sql
***************
*** 0 ****
--- 1,19 ----
+ CREATE OR REPLACE FUNCTION blhandler(internal)
+ RETURNS index_am_handler
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C;
+ 
+ -- Access method
+ CREATE ACCESS METHOD bloom TYPE INDEX HANDLER blhandler;
+ 
+ -- Opclasses
+ 
+ CREATE OPERATOR CLASS int4_ops
+ DEFAULT FOR TYPE int4 USING bloom AS
+ 	OPERATOR	1	=(int4, int4),
+ 	FUNCTION	1	hashint4(int4);
+ 
+ CREATE OPERATOR CLASS text_ops
+ DEFAULT FOR TYPE text USING bloom AS
+ 	OPERATOR	1	=(text, text),
+ 	FUNCTION	1	hashtext(text);
diff --git a/contrib/bloom/bloom.control b/contrib/bloom/bloom.control
new file mode 100644
index ...4d4124b
*** a/contrib/bloom/bloom.control
--- b/contrib/bloom/bloom.control
***************
*** 0 ****
--- 1,5 ----
+ # bloom extension
+ comment = 'bloom access method - signature file based index'
+ default_version = '1.0'
+ module_pathname = '$libdir/bloom'
+ relocatable = true
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
new file mode 100644
index ...6f20ee4
*** a/contrib/bloom/bloom.h
--- b/contrib/bloom/bloom.h
***************
*** 0 ****
--- 1,176 ----
+ /*-------------------------------------------------------------------------
+  *
+  * bloom.h
+  *	  Header for bloom index.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/bloom.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef _BLOOM_H_
+ #define _BLOOM_H_
+ 
+ #include "access/amapi.h"
+ #include "access/generic_xlog.h"
+ #include "access/itup.h"
+ #include "access/xlog.h"
+ #include "nodes/relation.h"
+ #include "fmgr.h"
+ 
+ /* Support procedures numbers */
+ #define	BLOOM_HASH_PROC			1
+ #define	BLOOM_NPROC				1
+ 
+ /* Scan strategies */
+ #define	BLOOM_EQUAL_STRATEGY	1
+ #define	BLOOM_NSTRATEGIES		1
+ 
+ /* Opaque for bloom pages */
+ typedef struct BloomPageOpaqueData
+ {
+ 	OffsetNumber	maxoff;
+ 	uint16			flags;
+ } BloomPageOpaqueData;
+ 
+ typedef BloomPageOpaqueData *BloomPageOpaque;
+ 
+ /* Bloom page flags */
+ #define BLOOM_META		(1<<0)
+ #define BLOOM_DELETED	(2<<0)
+ 
+ #define BloomPageGetOpaque(page) ((BloomPageOpaque) PageGetSpecialPointer(page))
+ #define BloomPageGetMaxOffset(page) (BloomPageGetOpaque(page)->maxoff)
+ #define BloomPageIsMeta(page) (BloomPageGetOpaque(page)->flags & BLOOM_META)
+ #define BloomPageIsDeleted(page) (BloomPageGetOpaque(page)->flags & BLOOM_DELETED)
+ #define BloomPageSetDeleted(page) (BloomPageGetOpaque(page)->flags |= BLOOM_DELETED)
+ #define BloomPageSetNonDeleted(page) (BloomPageGetOpaque(page)->flags &= ~BLOOM_DELETED)
+ #define BloomPageGetData(page)		((BloomTuple *)PageGetContents(page))
+ #define BloomPageGetTuple(state, page, offset) \
+ 	((BloomTuple *)(PageGetContents(page) \
+ 		+ (state)->sizeOfBloomTuple * ((offset) - 1)))
+ #define BloomPageGetNextTuple(state, tuple) \
+ 	((BloomTuple *)((Pointer)(tuple) + (state)->sizeOfBloomTuple))
+ 
+ /* Preserved page numbers */
+ #define BLOOM_METAPAGE_BLKNO	(0)
+ #define BLOOM_HEAD_BLKNO		(1) /* first data page */
+ 
+ /* Bloom index options */
+ typedef struct BloomOptions
+ {
+ 	int32	vl_len_;				 /* varlena header (do not touch directly!) */
+ 	int		bloomLength;			 /* length of signature in uint16 */
+ 	int		bitSize[INDEX_MAX_KEYS]; /* signature bits per index key */
+ } BloomOptions;
+ 
+ /*
+  * FreeBlockNumberArray - array of block numbers sized so that metadata fill
+  * all space in metapage.
+  */
+ typedef BlockNumber FreeBlockNumberArray[
+ 	MAXALIGN_DOWN(
+ 		BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(BloomPageOpaqueData))
+ 			   - MAXALIGN(sizeof(uint16) * 2 + sizeof(uint32) + sizeof(BloomOptions))
+ 	) / sizeof(BlockNumber) 
+ ];
+ 
+ /* Metadata of bloom index */
+ typedef struct BloomMetaPageData
+ {
+ 	uint32					magickNumber;
+ 	uint16					nStart;
+ 	uint16					nEnd;
+ 	BloomOptions			opts;
+ 	FreeBlockNumberArray	notFullPage;
+ } BloomMetaPageData;
+ 
+ /* Magic number to distinguish bloom pages among anothers */
+ #define BLOOM_MAGICK_NUMBER	(0xDBAC0DED)
+ 
+ /* Number of blocks numbers fit in BloomMetaPageData */
+ #define BloomMetaBlockN		(sizeof(FreeBlockNumberArray) / sizeof(BlockNumber))
+ 
+ #define BloomPageGetMeta(page)	((BloomMetaPageData *) PageGetContents(page))
+ 
+ typedef struct BloomState 
+ {
+ 	FmgrInfo			hashFn[INDEX_MAX_KEYS];
+ 	BloomOptions	   *opts; /* stored in rd_amcache and defined at creation time */
+ 	int32				nColumns;
+ 	/* 
+ 	 * sizeOfBloomTuple is index's specific, and it depends on
+ 	 * reloptions, so precompute it
+ 	 */
+ 	int32				sizeOfBloomTuple; 
+ } BloomState;
+ 
+ #define BloomPageGetFreeSpace(state, page) \
+ 	(BLCKSZ - MAXALIGN(SizeOfPageHeaderData) \
+ 		- BloomPageGetMaxOffset(page) * (state)->sizeOfBloomTuple \
+ 		- MAXALIGN(sizeof(BloomPageOpaqueData)))
+ 
+ /*
+  * Tuples are very different from all other relations
+  */
+ typedef uint16	SignType;
+ 
+ typedef struct BloomTuple
+ {
+ 	ItemPointerData		heapPtr;
+ 	SignType			sign[1];
+ } BloomTuple;
+ 
+ #define BLOOMTUPLEHDRSZ	offsetof(BloomTuple, sign)
+ 
+ /* Opaque data structure for bloom index scan */
+ typedef struct BloomScanOpaqueData
+ {
+ 	SignType   *sign;	/* Scan signature */
+ 	BloomState	state;
+ } BloomScanOpaqueData;
+ 
+ typedef BloomScanOpaqueData *BloomScanOpaque;
+ 
+ /* blutils.c */
+ extern void _PG_init(void);
+ extern Datum blhandler(PG_FUNCTION_ARGS);
+ extern void initBloomState(BloomState *state, Relation index);
+ extern void BloomInitMetapage(Relation index);
+ extern void BloomInitPage(Page page, uint16 flags);
+ extern Buffer BloomNewBuffer(Relation index);
+ extern void signValue(BloomState *state, SignType *sign, Datum value, int attno);
+ extern BloomTuple* BloomFormTuple(BloomState *state, ItemPointer iptr, Datum *values, bool *isnull);
+ extern bool BloomPageAddItem(BloomState *state, Page page, BloomTuple *tuple);
+ 
+ /* blvalidate.c */
+ extern bool blvalidate(Oid opclassoid);
+ 
+ /* interface functions */
+ extern bool blinsert(Relation index, Datum *values, bool *isnull,
+ 					 ItemPointer ht_ctid, Relation heapRel,
+ 					 IndexUniqueCheck checkUnique);
+ extern IndexScanDesc blbeginscan(Relation r, int nkeys, int norderbys);
+ extern int64 blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
+ extern void blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ 					 ScanKey orderbys, int norderbys);
+ extern void blendscan(IndexScanDesc scan);
+ extern void blmarkpos(IndexScanDesc scan);
+ extern void blrestrpos(IndexScanDesc scan);
+ extern IndexBuildResult *blbuild(Relation heap, Relation index,
+ 								   struct IndexInfo *indexInfo);
+ extern void blbuildempty(Relation index);
+ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
+ 	IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback,
+ 	void *callback_state);
+ extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
+ 	IndexBulkDeleteResult *stats);
+ extern bytea *bloptions(Datum reloptions, bool validate);
+ extern void blcostestimate(PlannerInfo *root, IndexPath *path,
+ 						   double loop_count, Cost *indexStartupCost,
+ 						   Cost *indexTotalCost, Selectivity *indexSelectivity,
+ 						   double *indexCorrelation);
+ 
+ #endif
diff --git a/contrib/bloom/blscan.c b/contrib/bloom/blscan.c
new file mode 100644
index ...467a1b1
*** a/contrib/bloom/blscan.c
--- b/contrib/bloom/blscan.c
***************
*** 0 ****
--- 1,174 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blscan.c
+  *		Bloom index scan functions.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blscan.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/relscan.h"
+ #include "pgstat.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/lmgr.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ 
+ #include "bloom.h"
+ 
+ IndexScanDesc
+ blbeginscan(Relation r, int nkeys, int norderbys)
+ {
+ 	IndexScanDesc scan;
+ 
+ 	scan = RelationGetIndexScan(r, nkeys, norderbys);
+ 
+ 	return scan;
+ }
+ 
+ void
+ blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ 							 ScanKey orderbys, int norderbys)
+ {
+ 	BloomScanOpaque so;
+ 
+ 	so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so == NULL)
+ 	{
+ 		/* if called from blbeginscan */
+ 		so = (BloomScanOpaque) palloc(sizeof(BloomScanOpaqueData));
+ 		initBloomState(&so->state, scan->indexRelation);
+ 		scan->opaque = so;
+ 
+ 	}
+ 	else
+ 	{
+ 		if (so->sign)
+ 			pfree(so->sign);
+ 	}
+ 	so->sign = NULL;
+ 
+ 	if (scankey && scan->numberOfKeys > 0)
+ 	{
+ 		memmove(scan->keyData, scankey,
+ 				scan->numberOfKeys * sizeof(ScanKeyData));
+ 	}
+ }
+ 
+ void
+ blendscan(IndexScanDesc scan)
+ {
+ 	BloomScanOpaque so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so->sign)
+ 		pfree(so->sign);
+ 	so->sign = NULL;
+ }
+ 
+ void
+ blmarkpos(IndexScanDesc scan)
+ {
+ 	elog(ERROR, "Bloom does not support mark/restore");
+ }
+ 
+ void
+ blrestrpos(IndexScanDesc scan)
+ {
+ 	elog(ERROR, "Bloom does not support mark/restore");
+ }
+ 
+ int64
+ blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
+ {
+ 	int64					ntids = 0;
+ 	BlockNumber				blkno = BLOOM_HEAD_BLKNO,
+ 							npages;
+ 	int						i;
+ 	BufferAccessStrategy	bas;
+ 	BloomScanOpaque 		so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so->sign == NULL && scan->numberOfKeys > 0)
+ 	{
+ 		/* New search: have to calculate search signature */
+ 		ScanKey skey = scan->keyData;
+ 
+ 		so->sign = palloc0(sizeof(SignType) * so->state.opts->bloomLength); 
+ 		
+ 		for(i = 0; i < scan->numberOfKeys; i++)
+ 		{
+ 			/*
+ 			 * Assume bloom-indexable operators to be strict, so nothing
+ 			 * could be found for NULL key.
+ 			 */
+ 			if (skey->sk_flags & SK_ISNULL)
+ 			{
+ 				pfree(so->sign);
+ 				so->sign = NULL;
+ 				return 0;
+ 			}
+ 
+ 			/* Add next value to the signature */
+ 			signValue(&so->state, so->sign, skey->sk_argument,
+ 					  skey->sk_attno - 1);
+ 
+ 			skey++;
+ 		}
+ 	}
+ 
+ 	/*
+ 	 * We're going to read the whole index. This is why we use appropriate
+ 	 * buffer access strategy.
+ 	 */
+ 	bas = GetAccessStrategy(BAS_BULKREAD);
+ 	npages = RelationGetNumberOfBlocks(scan->indexRelation);
+ 
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		Buffer 			buffer;
+ 		Page			page;
+ 
+ 		buffer = ReadBufferExtended(scan->indexRelation, MAIN_FORKNUM,
+ 									blkno, RBM_NORMAL, bas);
+ 
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 		page = BufferGetPage(buffer);
+ 
+ 		if (!BloomPageIsDeleted(page))
+ 		{
+ 			OffsetNumber offset, maxOffset = BloomPageGetMaxOffset(page);
+ 
+ 			for (offset = 1; offset <= maxOffset; offset++)
+ 			{
+ 				BloomTuple *itup = BloomPageGetTuple(&so->state, page, offset);
+ 				bool		res = true;
+ 
+ 				/* Check index signature with scan signature */
+ 				for (i = 0; res && i < so->state.opts->bloomLength; i++)
+ 				{
+ 					if ((itup->sign[i] & so->sign[i]) != so->sign[i])
+ 						res = false;
+ 				}
+ 
+ 				/* Add matching tuples to bitmap */
+ 				if (res)
+ 				{
+ 					tbm_add_tuples(tbm, &itup->heapPtr, 1, true);
+ 					ntids++;
+ 				}
+ 			}
+ 		}
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 		CHECK_FOR_INTERRUPTS();
+ 	}
+ 	FreeAccessStrategy(bas);
+ 
+ 	return ntids;
+ }
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
new file mode 100644
index ...a7ec0c5
*** a/contrib/bloom/blutils.c
--- b/contrib/bloom/blutils.c
***************
*** 0 ****
--- 1,390 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blutils.c
+  *		Bloom index utilities.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blutils.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/amapi.h"
+ #include "access/generic_xlog.h"
+ #include "catalog/index.h"
+ #include "storage/lmgr.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "utils/memutils.h"
+ #include "access/reloptions.h"
+ #include "storage/freespace.h"
+ #include "storage/indexfsm.h"
+ 
+ #include "bloom.h"
+ 
+ /* Signature dealing macros */
+ #define BITSIGNTYPE	(BITS_PER_BYTE * sizeof(SignType))
+ #define GETWORD(x,i) ( *( (SignType*)(x) + (int)( (i) / BITSIGNTYPE ) ) )
+ #define CLRBIT(x,i)   GETWORD(x,i) &= ~( 0x01 << ( (i) % BITSIGNTYPE ) )
+ #define SETBIT(x,i)   GETWORD(x,i) |=  ( 0x01 << ( (i) % BITSIGNTYPE ) )
+ #define GETBIT(x,i) ( (GETWORD(x,i) >> ( (i) % BITSIGNTYPE )) & 0x01 )
+ 
+ PG_FUNCTION_INFO_V1(blhandler);
+ 
+ static relopt_kind bl_relopt_kind;
+ 
+ /*
+  * Module initialize function: initilized relation options.
+  */
+ void 
+ _PG_init(void)
+ {
+ 	int		i;
+ 	char	buf[16];
+ 
+ 	bl_relopt_kind = add_reloption_kind();
+ 
+ 	add_int_reloption(bl_relopt_kind, "length",
+ 					  "Length of signature in uint16 type", 5, 1, 256);
+ 
+ 	for(i = 0; i < INDEX_MAX_KEYS;i ++)
+ 	{
+ 		snprintf(buf, 16, "col%d", i+1);
+ 		add_int_reloption(bl_relopt_kind, buf,
+ 						  "Number of bits for corresponding column", 2, 1, 2048);
+ 	}
+ }
+ 
+ /*
+  * Bloom handler function: return IndexAmRoutine with access method parameters
+  * and callbacks.
+  */
+ Datum
+ blhandler(PG_FUNCTION_ARGS)
+ {
+ 	IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
+ 
+ 	amroutine->amstrategies = 1;
+ 	amroutine->amsupport = 1;
+ 	amroutine->amcanorder = false;
+ 	amroutine->amcanorderbyop = false;
+ 	amroutine->amcanbackward = false;
+ 	amroutine->amcanunique = false;
+ 	amroutine->amcanmulticol = true;
+ 	amroutine->amoptionalkey = true;
+ 	amroutine->amsearcharray = false;
+ 	amroutine->amsearchnulls = false;
+ 	amroutine->amstorage = false;
+ 	amroutine->amclusterable = false;
+ 	amroutine->ampredlocks = false;
+ 	amroutine->amkeytype = 0;
+ 
+ 	amroutine->aminsert = blinsert;
+ 	amroutine->ambeginscan = blbeginscan;
+ 	amroutine->amgettuple = NULL;
+ 	amroutine->amgetbitmap = blgetbitmap;
+ 	amroutine->amrescan = blrescan;
+ 	amroutine->amendscan = blendscan;
+ 	amroutine->ammarkpos = blmarkpos;
+ 	amroutine->amrestrpos = blrestrpos;
+ 	amroutine->ambuild = blbuild;
+ 	amroutine->ambuildempty = blbuildempty;
+ 	amroutine->ambulkdelete = blbulkdelete;
+ 	amroutine->amvacuumcleanup = blvacuumcleanup;
+ 	amroutine->amcanreturn = NULL;
+ 	amroutine->amcostestimate = blcostestimate;
+ 	amroutine->amoptions = bloptions;
+ 	amroutine->amvalidate = blvalidate;
+ 
+ 	PG_RETURN_POINTER(amroutine);
+ }
+ 
+ void 
+ initBloomState(BloomState *state, Relation index)
+ {
+ 	int	i;
+ 
+ 	state->nColumns = index->rd_att->natts;
+ 
+ 	/* Initialize hash function for each attribute */
+ 	for (i = 0; i < index->rd_att->natts; i++)
+ 	{
+ 		fmgr_info_copy(&(state->hashFn[i]),
+ 						index_getprocinfo(index, i + 1, BLOOM_HASH_PROC),
+ 						CurrentMemoryContext);
+ 	}
+ 
+ 	/* Inititalize amcache if needed */
+ 	if (!index->rd_amcache)
+ 	{
+ 		Buffer				buffer;
+ 		Page				page;
+ 		BloomMetaPageData	*meta;
+ 		BloomOptions		*opts;
+ 
+ 		opts = MemoryContextAlloc(index->rd_indexcxt, sizeof(BloomOptions));
+ 
+ 		buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 
+ 		page = BufferGetPage(buffer);
+ 
+ 		if (!BloomPageIsMeta(page))
+ 			elog(ERROR, "Relation is not a bloom index");
+ 		meta = BloomPageGetMeta(BufferGetPage(buffer));
+ 
+ 		if (meta->magickNumber != BLOOM_MAGICK_NUMBER)
+ 			elog(ERROR, "Relation is not a bloom index");
+ 
+ 		*opts = meta->opts;
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 
+ 		index->rd_amcache = (void *)opts;
+ 	}
+ 
+ 	state->opts = (BloomOptions *)index->rd_amcache;
+ 	state->sizeOfBloomTuple = BLOOMTUPLEHDRSZ +
+ 									sizeof(SignType) * state->opts->bloomLength; 
+ }
+ 
+ /*
+  * Add bits of given value to the signature.
+  */
+ void
+ signValue(BloomState *state, SignType *sign, Datum value, int attno)
+ {
+ 	uint32		hashVal;
+ 	int 		nBit, j;
+ 
+ 	/*
+ 	 * init generator with "column's" number to get
+ 	 * "hashed" seed for new value. We don't want to map
+ 	 * the same numbers from different columns into the same bits!
+ 	 */
+ 	srand(attno);
+ 
+ 	/*
+ 	 * Init hash sequence to map our value into bits. the same values
+ 	 * in different columns will be mapped into different bits because
+ 	 * of step above
+ 	 */
+ 	hashVal = DatumGetInt32(FunctionCall1(&state->hashFn[attno], value));
+ 	srand(hashVal ^ rand());
+ 
+ 	for (j = 0; j < state->opts->bitSize[attno]; j++)
+ 	{
+ 		/* prevent mutiple evaluation */
+ 		nBit = rand() % (state->opts->bloomLength * BITSIGNTYPE); 
+ 		SETBIT(sign, nBit);
+ 	}
+ }
+ 
+ /*
+  * Make bloom tuple from values.
+  */
+ BloomTuple *
+ BloomFormTuple(BloomState *state, ItemPointer iptr, Datum *values, bool *isnull)
+ {
+ 	int 		i;
+ 	BloomTuple *res = (BloomTuple *)palloc0(state->sizeOfBloomTuple);
+ 
+ 	res->heapPtr = *iptr;
+ 
+     /*
+ 	 * Blooming
+ 	 */
+ 	for (i = 0; i < state->nColumns; i++)
+ 	{
+ 		/* skip nulls */
+ 		if (isnull[i])
+ 			continue;
+ 
+ 		signValue(state, res->sign, values[i], i);
+ 	}
+ 
+ 	return res;
+ }
+ 
+ /*
+  * Add new bloom tuple to the page.
+  */
+ bool
+ BloomPageAddItem(BloomState *state, Page page, BloomTuple *tuple)
+ {
+ 	BloomTuple		   *itup;
+ 	BloomPageOpaque		opaque;
+ 	Pointer				ptr;
+ 
+ 	if (BloomPageGetFreeSpace(state, page) < state->sizeOfBloomTuple)
+ 		return false;
+ 
+ 	/* Copy new tuple to the end of page */
+ 	opaque = BloomPageGetOpaque(page);
+ 	itup = BloomPageGetTuple(state, page, opaque->maxoff + 1);
+ 	memcpy((Pointer)itup, (Pointer)tuple, state->sizeOfBloomTuple);
+ 
+ 	/* Adjust maxoff and pd_lower */
+ 	opaque->maxoff++;
+ 	ptr = (Pointer)BloomPageGetTuple(state, page, opaque->maxoff + 1);
+ 	((PageHeader) page)->pd_lower = ptr - page;
+ 
+ 	return true;
+ }
+ 
+ /*
+  * Allocate a new page (either by recycling, or by extending the index file)
+  * The returned buffer is already pinned and exclusive-locked
+  * Caller is responsible for initializing the page by calling BloomInitBuffer
+  */
+ Buffer
+ BloomNewBuffer(Relation index)
+ {
+ 	Buffer      buffer;
+ 	bool        needLock;
+ 
+ 	/* First, try to get a page from FSM */
+ 	for (;;)
+ 	{
+ 		BlockNumber blkno = GetFreeIndexPage(index);
+ 
+ 		if (blkno == InvalidBlockNumber)
+ 			break;
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 
+ 		/*
+ 		 * We have to guard against the possibility that someone else already
+ 		 * recycled this page; the buffer may be locked if so.
+ 		 */
+ 		if (ConditionalLockBuffer(buffer))
+ 		{
+ 			Page	page = BufferGetPage(buffer);
+ 
+ 			if (PageIsNew(page))
+ 				return buffer;  /* OK to use, if never initialized */
+ 
+ 			if (BloomPageIsDeleted(page))
+ 				return buffer;  /* OK to use */
+ 
+ 			LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ 		}
+ 
+ 		/* Can't use it, so release buffer and try again */
+ 		ReleaseBuffer(buffer);
+ 	}
+ 
+ 	/* Must extend the file */
+ 	needLock = !RELATION_IS_LOCAL(index);
+ 	if (needLock)
+ 		LockRelationForExtension(index, ExclusiveLock);
+ 
+ 	buffer = ReadBuffer(index, P_NEW);
+ 	LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 	if (needLock)
+ 		UnlockRelationForExtension(index, ExclusiveLock);
+ 
+ 	return buffer;
+ }
+ 
+ /*
+  * Initialize bloom page.
+  */
+ void
+ BloomInitPage(Page page, uint16 flags)
+ {
+ 	BloomPageOpaque opaque;
+ 
+ 	PageInit(page, BLCKSZ, sizeof(BloomPageOpaqueData));
+ 
+ 	opaque = BloomPageGetOpaque(page);
+ 	memset(opaque, 0, sizeof(BloomPageOpaqueData));
+ 	opaque->maxoff = 0;
+ 	opaque->flags = flags;
+ }
+ 
+ static BloomOptions *
+ makeDefaultBloomOptions(BloomOptions *opts)
+ {
+ 	int i;
+ 
+ 	if (!opts)
+ 		opts = palloc0(sizeof(BloomOptions));
+ 
+ 	if (opts->bloomLength <= 0)
+ 		opts->bloomLength = 5;
+ 
+ 	for(i = 0; i < INDEX_MAX_KEYS; i++)
+ 		if (opts->bitSize[i] <= 0
+ 				|| opts->bitSize[i] >= opts->bloomLength * sizeof(SignType))
+ 			opts->bitSize[i] = 2;
+ 
+ 	return opts;
+ }
+ 
+ /*
+  * Initialize metapage for bloom index.
+  */
+ void
+ BloomInitMetapage(Relation index)
+ {
+ 	Page				metaPage;
+ 	Buffer				metaBuffer;
+ 	BloomMetaPageData  *metadata;
+ 
+ 	metaBuffer = BloomNewBuffer(index);
+ 	Assert(BufferGetBlockNumber(metaBuffer) == BLOOM_METAPAGE_BLKNO);
+ 
+ 	GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(metaBuffer, true);
+ 
+ 	BloomInitPage(metaPage, BLOOM_META);
+ 	metadata = BloomPageGetMeta(metaPage);
+ 	memset(metadata, 0, sizeof(BloomMetaPageData));
+ 	metadata->magickNumber = BLOOM_MAGICK_NUMBER;
+ 	metadata->opts = *makeDefaultBloomOptions((BloomOptions*)index->rd_options);
+ 	((PageHeader) metaPage)->pd_lower += sizeof(BloomMetaPageData);
+ 
+ 	GenericXLogFinish();
+ 	UnlockReleaseBuffer(metaBuffer);
+ }
+ 
+ /*
+  * Initialize options for bloom index.
+  */
+ bytea *
+ bloptions(Datum reloptions, bool validate)
+ {
+ 	relopt_value	   *options;
+ 	int					numoptions;
+ 	BloomOptions	   *rdopts;
+ 	relopt_parse_elt	tab[INDEX_MAX_KEYS + 1];
+ 	int					i;
+ 	char				buf[16];
+ 
+ 	tab[0].optname = "length";
+ 	tab[0].opttype = RELOPT_TYPE_INT;
+ 	tab[0].offset = offsetof(BloomOptions, bloomLength);
+ 
+ 	for(i = 0; i < INDEX_MAX_KEYS; i++)
+ 	{
+ 		snprintf(buf, sizeof(buf), "col%d", i + 1);
+ 		tab[i + 1].optname = pstrdup(buf);
+ 		tab[i + 1].opttype = RELOPT_TYPE_INT;
+ 		tab[i + 1].offset = offsetof(BloomOptions, bitSize[i]);
+ 	}
+ 
+ 	options = parseRelOptions(reloptions, validate, bl_relopt_kind, &numoptions);
+ 	rdopts = allocateReloptStruct(sizeof(BloomOptions), options, numoptions);
+ 	fillRelOptions((void *) rdopts, sizeof(BloomOptions), options, numoptions,
+ 						validate, tab, INDEX_MAX_KEYS + 1);
+ 		
+ 	rdopts = makeDefaultBloomOptions(rdopts);
+ 
+ 	return (bytea *)rdopts;
+ }
diff --git a/contrib/bloom/blvacuum.c b/contrib/bloom/blvacuum.c
new file mode 100644
index ...c694714
*** a/contrib/bloom/blvacuum.c
--- b/contrib/bloom/blvacuum.c
***************
*** 0 ****
--- 1,195 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blvacuum.c
+  *		Bloom VACUUM functions.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blvacuum.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "catalog/storage.h"
+ #include "commands/vacuum.h"
+ #include "miscadmin.h"
+ #include "postmaster/autovacuum.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "storage/lmgr.h"
+ 
+ #include "bloom.h"
+ 
+ IndexBulkDeleteResult *
+ blbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
+ 			 IndexBulkDeleteCallback callback, void *callback_state)
+ {
+ 	Relation				index = info->index;
+ 	BlockNumber				blkno,
+ 							npages;
+ 	FreeBlockNumberArray	notFullPage;
+ 	int						countPage = 0;
+ 	BloomState				state;
+ 	Buffer					buffer;
+ 	Page					page;
+ 
+ 	if (stats == NULL)
+ 		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+ 
+ 	initBloomState(&state, index); 
+ 
+ 	/*
+ 	 * Interate over the pages. We don't care about concurrently added pages,
+ 	 * they can't contain tuples to delete.
+ 	 */
+ 	npages = RelationGetNumberOfBlocks(index);
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		BloomTuple *itup, *itupPtr, *itupEnd;
+ 
+ 		buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
+ 									RBM_NORMAL, info->strategy);
+ 
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		GenericXLogStart(index);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		if (BloomPageIsDeleted(page))
+ 		{
+ 			UnlockReleaseBuffer(buffer);
+ 			CHECK_FOR_INTERRUPTS();
+ 			continue;
+ 		}
+ 
+ 		/* Iterate over the tuples */
+ 		itup = BloomPageGetTuple(&state, page, 1);
+ 		itupPtr = BloomPageGetTuple(&state, page, 1);
+ 		itupEnd = BloomPageGetTuple(&state, page, BloomPageGetMaxOffset(page) + 1);
+ 		while (itup < itupEnd)
+ 		{
+ 			/* Do we have to delete this tuple? */
+ 			if (callback(&itup->heapPtr, callback_state))
+ 			{
+ 				stats->tuples_removed += 1;
+ 				BloomPageGetOpaque(page)->maxoff--;
+ 			} 
+ 			else 
+ 			{
+ 				if (itupPtr != itup)
+ 				{
+ 					/*
+ 					 * If we already delete something before, we have to move
+ 					 * this tuple backward.
+ 					 */
+ 					memmove((Pointer)itupPtr, (Pointer)itup,
+ 							state.sizeOfBloomTuple);
+ 				}
+ 				stats->num_index_tuples++;
+ 				itupPtr = BloomPageGetNextTuple(&state, itupPtr);
+ 			}
+ 
+ 			itup = BloomPageGetNextTuple(&state, itup);
+ 		}
+ 
+ 		/* Did we delete something? */
+ 		if (itupPtr != itup)
+ 		{
+ 			/* Is it empty page now? */
+ 			if (itupPtr == BloomPageGetData(page))
+ 				BloomPageSetDeleted(page);
+ 			/* Adjust pg_lower */
+ 			((PageHeader) page)->pd_lower = (Pointer)itupPtr - page;
+ 			/* Finish WAL-logging */
+ 			GenericXLogFinish();
+ 		}
+ 		else
+ 		{
+ 			/* Didn't change anything: abort WAL-logging */
+ 			GenericXLogAbort();
+ 		}
+ 
+ 		if (!BloomPageIsDeleted(page) && 
+ 				BloomPageGetFreeSpace(&state, page) > state.sizeOfBloomTuple && 
+ 				countPage < BloomMetaBlockN)
+ 			notFullPage[countPage++] = blkno;
+ 		UnlockReleaseBuffer(buffer);
+ 		CHECK_FOR_INTERRUPTS();
+ 	}
+ 
+ 	if (countPage > 0)
+ 	{
+ 		BloomMetaPageData	*metaData;
+ 
+ 		buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 		GenericXLogStart(index);
+ 		page = GenericXLogRegister(buffer, false);
+ 
+ 		metaData = BloomPageGetMeta(page);
+ 		memcpy(metaData->notFullPage, notFullPage, sizeof(FreeBlockNumberArray));
+ 		metaData->nStart=0;
+ 		metaData->nEnd = countPage;
+ 
+ 		GenericXLogFinish();
+ 		UnlockReleaseBuffer(buffer);
+ 	}
+ 
+ 	return stats;
+ }
+ 
+ IndexBulkDeleteResult *
+ blvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
+ {
+ 	Relation	index = info->index;
+ 	BlockNumber npages,
+ 				blkno;
+ 	BlockNumber	totFreePages;
+ 
+ 	if (info->analyze_only)
+ 		return stats;
+ 
+ 	if (stats == NULL)
+ 		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+ 
+ 	/*
+ 	 * Iterate over the pages: insert deleted pages into FSM and collect
+ 	 * statistics.
+ 	 */
+ 	npages = RelationGetNumberOfBlocks(index);
+ 	totFreePages = 0;
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		Buffer		buffer;
+ 		Page		page;
+ 
+ 		vacuum_delay_point();
+ 
+ 		buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
+ 									RBM_NORMAL, info->strategy);
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 		page = (Page) BufferGetPage(buffer);
+ 
+ 		if (BloomPageIsDeleted(page))
+ 		{
+ 			RecordFreeIndexPage(index, blkno);
+ 			totFreePages++;
+ 		}
+ 		else
+ 		{
+ 			stats->num_index_tuples += BloomPageGetMaxOffset(page);
+ 			stats->estimated_count += BloomPageGetMaxOffset(page);
+ 		}
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 	}
+ 
+ 	IndexFreeSpaceMapVacuum(info->index);
+ 	stats->pages_free = totFreePages;
+ 	stats->num_pages = RelationGetNumberOfBlocks(index);
+ 
+ 	return stats;
+ }
diff --git a/contrib/bloom/blvalidate.c b/contrib/bloom/blvalidate.c
new file mode 100644
index ...5344b81
*** a/contrib/bloom/blvalidate.c
--- b/contrib/bloom/blvalidate.c
***************
*** 0 ****
--- 1,220 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blvalidate.c
+  *	  Opclass validator for bloom.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blvalidate.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/amvalidate.h"
+ #include "access/htup_details.h"
+ #include "catalog/pg_amop.h"
+ #include "catalog/pg_amproc.h"
+ #include "catalog/pg_opclass.h"
+ #include "catalog/pg_opfamily.h"
+ #include "catalog/pg_type.h"
+ #include "utils/builtins.h"
+ #include "utils/lsyscache.h"
+ #include "utils/syscache.h"
+ 
+ #include "bloom.h"
+ 
+ /*
+  * Validator for a bloom opclass.
+  */
+ bool
+ blvalidate(Oid opclassoid)
+ {
+ 	bool		result = true;
+ 	HeapTuple	classtup;
+ 	Form_pg_opclass classform;
+ 	Oid			opfamilyoid;
+ 	Oid			opcintype;
+ 	Oid			opckeytype;
+ 	char	   *opclassname;
+ 	HeapTuple	familytup;
+ 	Form_pg_opfamily familyform;
+ 	char	   *opfamilyname;
+ 	CatCList   *proclist,
+ 			   *oprlist;
+ 	List	   *grouplist;
+ 	OpFamilyOpFuncGroup *opclassgroup;
+ 	int			i;
+ 	ListCell   *lc;
+ 
+ 	/* Fetch opclass information */
+ 	classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
+ 	if (!HeapTupleIsValid(classtup))
+ 		elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
+ 	classform = (Form_pg_opclass) GETSTRUCT(classtup);
+ 
+ 	opfamilyoid = classform->opcfamily;
+ 	opcintype = classform->opcintype;
+ 	opckeytype = classform->opckeytype;
+ 	if (!OidIsValid(opckeytype))
+ 		opckeytype = opcintype;
+ 	opclassname = NameStr(classform->opcname);
+ 
+ 	/* Fetch opfamily information */
+ 	familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
+ 	if (!HeapTupleIsValid(familytup))
+ 		elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
+ 	familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
+ 
+ 	opfamilyname = NameStr(familyform->opfname);
+ 
+ 	/* Fetch all operators and support functions of the opfamily */
+ 	oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
+ 	proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
+ 
+ 	/* Check individual support functions */
+ 	for (i = 0; i < proclist->n_members; i++)
+ 	{
+ 		HeapTuple	proctup = &proclist->members[i]->tuple;
+ 		Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
+ 		bool		ok;
+ 
+ 		/*
+ 		 * All bloom support functions should be registered with matching
+ 		 * left/right types
+ 		 */
+ 		if (procform->amproclefttype != procform->amprocrighttype)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains support procedure %s with cross-type registration",
+ 							opfamilyname,
+ 							format_procedure(procform->amproc))));
+ 			result = false;
+ 		}
+ 
+ 		/*
+ 		 * We can't check signatures except within the specific opclass, since
+ 		 * we need to know the associated opckeytype in many cases.
+ 		 */
+ 		if (procform->amproclefttype != opcintype)
+ 			continue;
+ 
+ 		/* Check procedure numbers and function signatures */
+ 		switch (procform->amprocnum)
+ 		{
+ 			case BLOOM_HASH_PROC:
+ 				ok = check_amproc_signature(procform->amproc, INT4OID, false,
+ 											1, 1, opckeytype);
+ 				break;
+ 			default:
+ 				ereport(INFO,
+ 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 						 errmsg("bloom opfamily %s contains function %s with invalid support number %d",
+ 								opfamilyname,
+ 								format_procedure(procform->amproc),
+ 								procform->amprocnum)));
+ 				result = false;
+ 				continue;		/* don't want additional message */
+ 		}
+ 
+ 		if (!ok)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("gist opfamily %s contains function %s with wrong signature for support number %d",
+ 							opfamilyname,
+ 							format_procedure(procform->amproc),
+ 							procform->amprocnum)));
+ 			result = false;
+ 		}
+ 	}
+ 
+ 	/* Check individual operators */
+ 	for (i = 0; i < oprlist->n_members; i++)
+ 	{
+ 		HeapTuple	oprtup = &oprlist->members[i]->tuple;
+ 		Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
+ 
+ 		/* Check it's allowed strategy for bloom */
+ 		if (oprform->amopstrategy < 1 || 
+ 			oprform->amopstrategy > BLOOM_NSTRATEGIES)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains operator %s with invalid strategy number %d",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr),
+ 							oprform->amopstrategy)));
+ 			result = false;
+ 		}
+ 
+ 		/* bloom doesn't support ORDER BY operators */
+ 		if (oprform->amoppurpose != AMOP_SEARCH ||
+ 			OidIsValid(oprform->amopsortfamily))
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains invalid ORDER BY specification for operator %s",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr))));
+ 			result = false;
+ 		}
+ 
+ 		/* Check operator signature --- same for all bloom strategies */
+ 		if (!check_amop_signature(oprform->amopopr, BOOLOID,
+ 								  oprform->amoplefttype,
+ 								  oprform->amoprighttype))
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains operator %s with wrong signature",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr))));
+ 			result = false;
+ 		}
+ 	}
+ 
+ 	/* Now check for inconsistent groups of operators/functions */
+ 	grouplist = identify_opfamily_groups(oprlist, proclist);
+ 	opclassgroup = NULL;
+ 	foreach(lc, grouplist)
+ 	{
+ 		OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
+ 
+ 		/* Remember the group exactly matching the test opclass */
+ 		if (thisgroup->lefttype == opcintype &&
+ 			thisgroup->righttype == opcintype)
+ 			opclassgroup = thisgroup;
+ 
+ 		/*
+ 		 * There is not a lot we can do to check the operator sets, since each
+ 		 * bloom opclass is more or less a law unto itself, and some contain
+ 		 * only operators that are binary-compatible with the opclass datatype
+ 		 * (meaning that empty operator sets can be OK).  That case also means
+ 		 * that we shouldn't insist on nonempty function sets except for the
+ 		 * opclass's own group.
+ 		 */
+ 	}
+ 
+ 	/* Check that the originally-named opclass is complete */
+ 	for (i = 1; i <= BLOOM_NPROC; i++)
+ 	{
+ 		if (opclassgroup &&
+ 			(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
+ 			continue;			/* got it */
+ 		ereport(INFO,
+ 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 				 errmsg("bloom opclass %s is missing support function %d",
+ 						opclassname, i)));
+ 		result = false;
+ 	}
+ 
+ 	ReleaseCatCacheList(proclist);
+ 	ReleaseCatCacheList(oprlist);
+ 	ReleaseSysCache(familytup);
+ 	ReleaseSysCache(classtup);
+ 
+ 	return result;
+ }
diff --git a/contrib/bloom/data/data b/contrib/bloom/data/data
new file mode 100644
index ...eacf3e7
*** a/contrib/bloom/data/data
--- b/contrib/bloom/data/data
***************
*** 0 ****
--- 1,10000 ----
+ 739	3
+ 475	9
+ 45	6
+ 433	1
+ 948	8
+ 926	8
+ 397	7
+ 980	4
+ 212	5
+ 522	9
+ 74	8
+ 77	4
+ 378	9
+ 575	3
+ 625	2
+ 407	4
+ 509	9
+ 252	6
+ 487	7
+ 656	4
+ 485	6
+ 275	9
+ 285	3
+ 277	5
+ 804	8
+ 424	9
+ 553	5
+ 245	9
+ 384	8
+ 202	0
+ 43	6
+ 374	6
+ 490	6
+ 105	10
+ 311	8
+ 411	8
+ 343	7
+ 678	6
+ 942	10
+ 126	7
+ 755	6
+ 625	3
+ 52	9
+ 239	4
+ 690	4
+ 445	7
+ 49	8
+ 285	5
+ 445	4
+ 516	8
+ 151	9
+ 553	5
+ 613	2
+ 123	6
+ 187	2
+ 301	9
+ 800	9
+ 250	9
+ 796	5
+ 288	5
+ 930	7
+ 219	10
+ 553	5
+ 518	10
+ 893	0
+ 754	0
+ 960	3
+ 538	6
+ 537	7
+ 127	7
+ 910	4
+ 666	7
+ 354	9
+ 562	1
+ 405	8
+ 635	3
+ 583	9
+ 313	1
+ 358	8
+ 133	3
+ 864	9
+ 296	8
+ 193	8
+ 396	7
+ 495	5
+ 454	4
+ 952	1
+ 115	3
+ 36	7
+ 455	4
+ 527	1
+ 775	1
+ 945	1
+ 246	3
+ 920	4
+ 554	8
+ 267	8
+ 608	5
+ 684	0
+ 190	2
+ 527	6
+ 584	5
+ 764	7
+ 785	8
+ 376	2
+ 240	9
+ 330	0
+ 13	3
+ 103	3
+ 578	0
+ 639	1
+ 807	9
+ 982	4
+ 365	7
+ 418	6
+ 844	9
+ 199	4
+ 425	10
+ 127	2
+ 762	5
+ 450	0
+ 406	8
+ 17	4
+ 55	1
+ 678	6
+ 143	3
+ 764	9
+ 222	7
+ 364	6
+ 411	8
+ 142	3
+ 728	3
+ 684	2
+ 303	8
+ 363	1
+ 314	8
+ 67	7
+ 592	1
+ 139	6
+ 204	8
+ 279	3
+ 133	0
+ 296	4
+ 788	7
+ 942	2
+ 441	1
+ 454	2
+ 424	1
+ 322	7
+ 949	7
+ 793	3
+ 497	9
+ 984	1
+ 944	1
+ 736	1
+ 940	0
+ 494	1
+ 57	8
+ 429	8
+ 449	4
+ 44	9
+ 454	5
+ 60	9
+ 636	4
+ 606	6
+ 67	4
+ 848	6
+ 259	8
+ 654	2
+ 955	4
+ 351	9
+ 405	8
+ 968	5
+ 634	4
+ 308	1
+ 767	4
+ 974	2
+ 850	0
+ 99	5
+ 416	7
+ 71	5
+ 103	9
+ 48	4
+ 750	7
+ 565	7
+ 92	9
+ 599	5
+ 760	6
+ 960	4
+ 964	3
+ 478	7
+ 620	5
+ 953	5
+ 485	1
+ 957	9
+ 756	0
+ 383	9
+ 946	4
+ 222	7
+ 133	8
+ 401	2
+ 702	0
+ 723	5
+ 568	7
+ 857	5
+ 951	3
+ 264	6
+ 786	2
+ 42	3
+ 268	10
+ 172	0
+ 26	6
+ 884	10
+ 986	1
+ 667	1
+ 893	1
+ 344	6
+ 68	1
+ 58	6
+ 750	9
+ 168	7
+ 249	4
+ 273	0
+ 649	3
+ 306	9
+ 314	5
+ 942	3
+ 33	8
+ 311	0
+ 932	10
+ 138	8
+ 47	5
+ 420	1
+ 550	5
+ 751	3
+ 392	9
+ 1	6
+ 351	3
+ 677	10
+ 588	10
+ 917	9
+ 461	9
+ 242	5
+ 685	6
+ 514	6
+ 531	7
+ 442	6
+ 135	9
+ 693	7
+ 341	4
+ 984	7
+ 362	10
+ 375	7
+ 259	1
+ 713	8
+ 35	6
+ 750	5
+ 489	10
+ 991	2
+ 544	5
+ 791	1
+ 156	2
+ 653	3
+ 96	3
+ 976	4
+ 789	10
+ 170	2
+ 946	5
+ 865	2
+ 597	6
+ 53	6
+ 209	8
+ 128	7
+ 794	1
+ 871	3
+ 623	7
+ 413	8
+ 895	1
+ 70	10
+ 411	0
+ 428	2
+ 6	6
+ 352	10
+ 143	2
+ 156	7
+ 795	2
+ 372	0
+ 11	5
+ 701	8
+ 619	6
+ 142	2
+ 233	6
+ 20	1
+ 621	1
+ 118	0
+ 136	5
+ 232	1
+ 145	6
+ 93	3
+ 800	2
+ 28	6
+ 457	4
+ 598	5
+ 900	3
+ 273	5
+ 870	4
+ 760	1
+ 970	8
+ 231	6
+ 871	3
+ 622	0
+ 895	9
+ 148	0
+ 439	2
+ 328	2
+ 489	4
+ 834	9
+ 756	4
+ 415	7
+ 730	7
+ 175	6
+ 102	9
+ 703	1
+ 715	9
+ 662	6
+ 283	3
+ 592	2
+ 139	7
+ 218	6
+ 981	5
+ 816	5
+ 902	7
+ 417	7
+ 82	8
+ 314	8
+ 519	5
+ 413	6
+ 424	1
+ 693	1
+ 50	4
+ 725	3
+ 639	3
+ 511	8
+ 58	7
+ 356	0
+ 275	2
+ 510	2
+ 822	9
+ 835	9
+ 759	1
+ 717	3
+ 639	1
+ 899	1
+ 246	6
+ 202	3
+ 946	9
+ 629	6
+ 245	1
+ 363	3
+ 870	7
+ 342	1
+ 891	9
+ 322	7
+ 778	2
+ 617	5
+ 307	3
+ 814	9
+ 463	7
+ 8	7
+ 304	2
+ 5	2
+ 138	6
+ 835	4
+ 774	2
+ 685	6
+ 916	0
+ 789	8
+ 878	1
+ 519	7
+ 269	1
+ 193	6
+ 470	0
+ 522	9
+ 720	5
+ 643	0
+ 741	6
+ 275	9
+ 282	1
+ 261	1
+ 306	9
+ 701	2
+ 973	5
+ 28	9
+ 602	5
+ 507	9
+ 683	7
+ 448	2
+ 708	10
+ 88	4
+ 501	7
+ 453	2
+ 379	7
+ 120	7
+ 836	4
+ 717	1
+ 327	4
+ 365	3
+ 908	4
+ 151	5
+ 940	7
+ 381	6
+ 359	8
+ 777	1
+ 799	9
+ 495	3
+ 595	9
+ 542	10
+ 675	7
+ 634	5
+ 44	4
+ 654	4
+ 769	0
+ 671	7
+ 411	8
+ 187	4
+ 481	6
+ 974	8
+ 398	8
+ 907	2
+ 615	4
+ 497	2
+ 349	0
+ 183	0
+ 701	8
+ 535	7
+ 169	2
+ 116	9
+ 208	8
+ 615	6
+ 610	8
+ 970	1
+ 371	9
+ 931	8
+ 695	8
+ 966	3
+ 239	5
+ 520	6
+ 502	7
+ 612	2
+ 520	1
+ 948	7
+ 337	1
+ 627	5
+ 852	2
+ 165	5
+ 45	1
+ 554	4
+ 79	5
+ 185	8
+ 323	2
+ 84	6
+ 613	6
+ 151	1
+ 306	8
+ 318	8
+ 911	3
+ 516	2
+ 331	1
+ 793	2
+ 385	10
+ 646	4
+ 92	2
+ 846	2
+ 686	0
+ 945	0
+ 181	0
+ 572	8
+ 633	7
+ 909	9
+ 486	2
+ 766	4
+ 493	3
+ 645	8
+ 424	4
+ 8	8
+ 395	7
+ 240	5
+ 855	1
+ 659	5
+ 117	6
+ 551	3
+ 634	1
+ 93	3
+ 846	0
+ 206	3
+ 228	10
+ 730	7
+ 253	4
+ 546	7
+ 813	6
+ 487	2
+ 209	7
+ 696	1
+ 814	4
+ 605	9
+ 959	2
+ 230	6
+ 278	3
+ 860	1
+ 324	1
+ 457	6
+ 37	2
+ 273	3
+ 561	8
+ 968	4
+ 373	5
+ 582	6
+ 183	3
+ 646	10
+ 633	3
+ 928	6
+ 407	2
+ 185	7
+ 480	0
+ 810	8
+ 111	3
+ 355	1
+ 453	6
+ 439	0
+ 447	4
+ 388	8
+ 862	10
+ 402	0
+ 247	0
+ 42	9
+ 299	10
+ 472	7
+ 127	7
+ 392	6
+ 702	2
+ 410	8
+ 468	8
+ 960	9
+ 394	4
+ 935	8
+ 806	3
+ 661	7
+ 292	1
+ 714	5
+ 111	8
+ 419	4
+ 725	9
+ 117	9
+ 548	5
+ 460	2
+ 711	9
+ 62	2
+ 636	0
+ 99	0
+ 421	0
+ 870	2
+ 357	5
+ 896	6
+ 594	6
+ 189	7
+ 365	6
+ 116	1
+ 499	2
+ 943	0
+ 742	4
+ 297	5
+ 273	4
+ 631	9
+ 382	7
+ 938	8
+ 765	8
+ 30	1
+ 338	9
+ 771	9
+ 535	10
+ 637	9
+ 568	8
+ 990	1
+ 986	9
+ 114	7
+ 335	4
+ 181	6
+ 770	8
+ 517	2
+ 543	5
+ 954	3
+ 262	10
+ 430	6
+ 910	2
+ 531	4
+ 160	2
+ 345	7
+ 921	3
+ 795	9
+ 267	9
+ 635	6
+ 319	8
+ 211	1
+ 628	7
+ 240	2
+ 182	2
+ 479	4
+ 179	9
+ 43	1
+ 110	6
+ 535	3
+ 743	9
+ 999	7
+ 214	8
+ 571	5
+ 702	2
+ 85	0
+ 22	3
+ 111	6
+ 23	4
+ 821	2
+ 546	3
+ 648	7
+ 210	7
+ 814	3
+ 267	3
+ 590	0
+ 228	6
+ 674	4
+ 382	2
+ 924	1
+ 450	0
+ 106	5
+ 304	2
+ 121	3
+ 568	9
+ 532	1
+ 243	2
+ 839	5
+ 872	7
+ 772	1
+ 2	4
+ 148	2
+ 950	8
+ 673	3
+ 66	6
+ 416	5
+ 606	5
+ 987	9
+ 739	1
+ 237	3
+ 51	8
+ 422	3
+ 949	3
+ 746	8
+ 914	5
+ 959	9
+ 880	1
+ 146	8
+ 929	8
+ 163	10
+ 416	6
+ 511	0
+ 102	5
+ 932	8
+ 606	2
+ 149	7
+ 939	6
+ 951	9
+ 831	7
+ 710	7
+ 215	7
+ 662	1
+ 777	8
+ 926	7
+ 627	1
+ 701	0
+ 668	2
+ 66	8
+ 709	10
+ 611	3
+ 168	8
+ 973	1
+ 330	9
+ 996	2
+ 620	7
+ 907	8
+ 375	6
+ 931	2
+ 377	9
+ 857	0
+ 945	6
+ 49	6
+ 769	1
+ 384	5
+ 113	10
+ 794	3
+ 756	8
+ 389	1
+ 690	4
+ 248	3
+ 90	2
+ 146	5
+ 724	1
+ 616	1
+ 933	5
+ 106	9
+ 31	2
+ 492	8
+ 270	9
+ 279	4
+ 872	1
+ 664	6
+ 840	1
+ 713	5
+ 438	10
+ 841	5
+ 116	10
+ 994	8
+ 63	6
+ 942	10
+ 83	0
+ 874	1
+ 203	4
+ 914	5
+ 242	2
+ 856	1
+ 265	5
+ 742	1
+ 573	5
+ 635	0
+ 416	5
+ 540	5
+ 462	5
+ 373	5
+ 143	3
+ 520	2
+ 363	4
+ 340	6
+ 760	3
+ 40	0
+ 446	9
+ 117	7
+ 416	9
+ 816	10
+ 313	5
+ 0	7
+ 927	5
+ 261	4
+ 74	6
+ 914	2
+ 949	4
+ 443	3
+ 829	8
+ 879	6
+ 37	9
+ 591	5
+ 814	7
+ 195	2
+ 566	0
+ 218	9
+ 462	2
+ 608	4
+ 759	9
+ 778	8
+ 504	7
+ 49	5
+ 127	5
+ 765	10
+ 276	6
+ 544	3
+ 562	1
+ 797	4
+ 843	10
+ 605	4
+ 2	8
+ 288	5
+ 42	9
+ 853	8
+ 765	6
+ 633	3
+ 324	7
+ 722	5
+ 175	5
+ 406	5
+ 130	9
+ 765	7
+ 85	6
+ 68	9
+ 553	7
+ 337	6
+ 497	6
+ 19	5
+ 520	9
+ 340	3
+ 504	10
+ 554	8
+ 656	3
+ 279	8
+ 763	7
+ 283	9
+ 634	0
+ 585	7
+ 609	7
+ 647	2
+ 326	10
+ 717	8
+ 608	7
+ 362	1
+ 608	7
+ 413	1
+ 676	10
+ 940	3
+ 244	2
+ 163	0
+ 903	4
+ 899	5
+ 494	5
+ 256	1
+ 136	9
+ 264	5
+ 886	10
+ 285	5
+ 717	6
+ 621	3
+ 349	0
+ 436	0
+ 2	4
+ 356	2
+ 595	5
+ 251	5
+ 965	2
+ 34	5
+ 633	3
+ 562	8
+ 192	8
+ 231	1
+ 807	5
+ 571	5
+ 163	2
+ 848	5
+ 226	3
+ 536	2
+ 661	9
+ 473	3
+ 412	7
+ 753	4
+ 874	8
+ 837	5
+ 77	4
+ 277	3
+ 225	5
+ 347	0
+ 24	9
+ 555	2
+ 109	4
+ 699	3
+ 688	2
+ 563	3
+ 128	0
+ 604	5
+ 759	4
+ 919	6
+ 143	8
+ 141	2
+ 154	4
+ 488	4
+ 926	8
+ 410	10
+ 752	10
+ 137	9
+ 369	8
+ 197	1
+ 72	8
+ 405	2
+ 795	0
+ 741	6
+ 365	7
+ 187	5
+ 415	3
+ 728	6
+ 745	2
+ 948	7
+ 51	4
+ 621	8
+ 324	8
+ 665	7
+ 595	9
+ 750	7
+ 622	2
+ 867	4
+ 164	6
+ 971	5
+ 267	2
+ 38	7
+ 485	8
+ 251	2
+ 982	2
+ 902	0
+ 556	5
+ 836	9
+ 282	5
+ 573	9
+ 364	3
+ 543	10
+ 477	4
+ 403	6
+ 18	4
+ 171	3
+ 531	2
+ 966	0
+ 974	2
+ 247	10
+ 415	1
+ 988	10
+ 672	8
+ 851	10
+ 325	4
+ 830	7
+ 746	4
+ 675	2
+ 784	1
+ 865	8
+ 450	0
+ 86	10
+ 244	1
+ 998	2
+ 269	2
+ 173	7
+ 394	2
+ 655	1
+ 986	5
+ 20	3
+ 930	8
+ 0	7
+ 224	7
+ 900	0
+ 752	8
+ 809	2
+ 800	9
+ 184	0
+ 947	2
+ 261	2
+ 427	4
+ 899	8
+ 596	6
+ 887	6
+ 60	9
+ 894	10
+ 757	9
+ 667	10
+ 569	6
+ 987	3
+ 331	8
+ 524	1
+ 691	7
+ 174	6
+ 891	4
+ 854	3
+ 870	8
+ 139	5
+ 307	0
+ 48	4
+ 933	9
+ 358	7
+ 836	0
+ 670	4
+ 591	7
+ 726	9
+ 454	3
+ 53	1
+ 959	2
+ 783	8
+ 663	6
+ 168	5
+ 389	3
+ 999	7
+ 334	0
+ 64	3
+ 989	4
+ 957	8
+ 447	6
+ 231	0
+ 285	10
+ 960	7
+ 208	0
+ 883	2
+ 240	7
+ 16	9
+ 302	2
+ 435	7
+ 490	4
+ 388	8
+ 481	5
+ 91	5
+ 874	0
+ 296	3
+ 675	5
+ 359	10
+ 484	3
+ 698	7
+ 332	6
+ 858	6
+ 247	9
+ 475	5
+ 57	9
+ 241	5
+ 344	6
+ 371	8
+ 81	5
+ 296	10
+ 509	6
+ 277	2
+ 120	6
+ 143	6
+ 955	8
+ 296	3
+ 421	2
+ 860	7
+ 28	3
+ 217	1
+ 244	5
+ 632	6
+ 87	0
+ 414	2
+ 465	7
+ 123	10
+ 303	4
+ 158	4
+ 36	3
+ 27	10
+ 142	3
+ 278	6
+ 476	1
+ 231	5
+ 472	4
+ 588	7
+ 907	2
+ 305	10
+ 223	7
+ 161	7
+ 428	3
+ 662	7
+ 684	8
+ 154	7
+ 121	2
+ 711	3
+ 503	10
+ 826	10
+ 127	1
+ 483	6
+ 506	1
+ 316	4
+ 292	6
+ 407	5
+ 339	6
+ 203	8
+ 853	9
+ 499	5
+ 684	7
+ 257	8
+ 833	10
+ 68	3
+ 958	9
+ 316	1
+ 950	8
+ 685	5
+ 870	0
+ 869	2
+ 622	3
+ 676	10
+ 844	9
+ 729	7
+ 743	2
+ 234	4
+ 881	5
+ 233	7
+ 460	3
+ 51	4
+ 193	4
+ 503	1
+ 165	2
+ 600	0
+ 189	5
+ 197	8
+ 745	9
+ 773	6
+ 752	5
+ 285	5
+ 730	5
+ 923	6
+ 10	2
+ 324	5
+ 455	4
+ 888	6
+ 741	4
+ 792	9
+ 579	4
+ 942	8
+ 862	1
+ 580	6
+ 12	4
+ 196	8
+ 854	5
+ 259	6
+ 1	2
+ 195	0
+ 336	5
+ 481	8
+ 894	4
+ 440	6
+ 760	2
+ 542	3
+ 625	5
+ 107	5
+ 622	7
+ 94	6
+ 40	3
+ 397	9
+ 771	7
+ 479	8
+ 837	7
+ 783	2
+ 192	3
+ 964	1
+ 633	4
+ 721	4
+ 637	3
+ 732	3
+ 746	8
+ 749	4
+ 527	8
+ 2	6
+ 133	4
+ 462	9
+ 54	9
+ 677	9
+ 613	5
+ 64	8
+ 725	0
+ 891	4
+ 433	6
+ 751	1
+ 876	5
+ 332	6
+ 324	1
+ 990	9
+ 925	10
+ 418	1
+ 390	9
+ 962	4
+ 820	6
+ 335	4
+ 99	4
+ 239	8
+ 427	1
+ 182	9
+ 743	9
+ 930	6
+ 418	3
+ 241	7
+ 344	2
+ 593	3
+ 223	0
+ 326	6
+ 891	3
+ 58	7
+ 928	4
+ 145	0
+ 792	4
+ 851	2
+ 514	0
+ 80	3
+ 967	0
+ 875	4
+ 272	1
+ 126	6
+ 347	7
+ 884	6
+ 730	2
+ 184	6
+ 498	2
+ 333	4
+ 635	5
+ 453	4
+ 861	3
+ 647	4
+ 338	7
+ 632	3
+ 736	5
+ 689	0
+ 624	8
+ 623	10
+ 534	5
+ 541	3
+ 717	7
+ 885	2
+ 967	2
+ 641	6
+ 696	1
+ 29	6
+ 399	7
+ 933	7
+ 403	6
+ 41	1
+ 73	7
+ 147	7
+ 546	8
+ 668	1
+ 278	2
+ 344	10
+ 934	2
+ 209	9
+ 447	8
+ 503	1
+ 944	5
+ 700	3
+ 208	6
+ 79	6
+ 198	1
+ 750	3
+ 851	9
+ 967	4
+ 668	6
+ 477	9
+ 843	8
+ 940	8
+ 51	1
+ 678	5
+ 999	2
+ 641	9
+ 713	3
+ 285	9
+ 974	4
+ 532	2
+ 485	3
+ 442	3
+ 179	4
+ 733	8
+ 44	2
+ 792	9
+ 32	7
+ 664	1
+ 880	3
+ 581	9
+ 523	2
+ 822	2
+ 563	1
+ 157	5
+ 471	7
+ 709	10
+ 971	2
+ 292	2
+ 561	0
+ 997	6
+ 236	8
+ 491	3
+ 521	2
+ 351	4
+ 498	9
+ 281	0
+ 153	1
+ 258	7
+ 209	4
+ 253	7
+ 105	10
+ 636	1
+ 113	9
+ 227	7
+ 954	2
+ 278	2
+ 14	8
+ 459	5
+ 926	8
+ 937	4
+ 742	2
+ 446	9
+ 320	7
+ 611	5
+ 120	9
+ 210	2
+ 827	8
+ 301	9
+ 775	5
+ 614	7
+ 753	9
+ 918	8
+ 663	4
+ 302	6
+ 187	2
+ 13	9
+ 457	5
+ 824	8
+ 163	4
+ 307	3
+ 300	5
+ 508	1
+ 363	8
+ 67	1
+ 338	7
+ 866	1
+ 573	8
+ 858	2
+ 161	2
+ 824	3
+ 399	8
+ 277	9
+ 295	1
+ 633	5
+ 536	9
+ 742	8
+ 456	3
+ 963	8
+ 61	0
+ 956	4
+ 710	8
+ 490	3
+ 606	3
+ 519	8
+ 508	3
+ 116	9
+ 179	4
+ 762	5
+ 494	4
+ 934	0
+ 335	7
+ 867	8
+ 926	8
+ 610	10
+ 859	6
+ 386	6
+ 389	9
+ 852	10
+ 224	4
+ 763	7
+ 713	9
+ 638	9
+ 272	4
+ 367	8
+ 796	3
+ 796	1
+ 977	7
+ 921	9
+ 493	5
+ 890	4
+ 98	3
+ 921	5
+ 152	8
+ 482	4
+ 143	2
+ 108	9
+ 124	7
+ 750	4
+ 147	1
+ 162	9
+ 418	10
+ 73	4
+ 622	10
+ 298	1
+ 526	2
+ 466	6
+ 464	4
+ 111	6
+ 159	6
+ 992	3
+ 837	1
+ 159	10
+ 847	9
+ 357	10
+ 26	5
+ 937	4
+ 478	0
+ 839	1
+ 5	1
+ 214	5
+ 325	7
+ 156	8
+ 66	3
+ 405	2
+ 859	4
+ 527	7
+ 498	7
+ 658	3
+ 595	0
+ 339	6
+ 535	3
+ 65	0
+ 286	9
+ 112	3
+ 41	3
+ 823	4
+ 6	10
+ 154	1
+ 245	6
+ 295	1
+ 957	8
+ 800	5
+ 508	5
+ 801	1
+ 473	1
+ 723	0
+ 415	8
+ 21	7
+ 691	1
+ 993	7
+ 460	8
+ 97	5
+ 795	3
+ 536	0
+ 811	8
+ 144	8
+ 654	9
+ 224	2
+ 403	0
+ 263	9
+ 165	10
+ 884	6
+ 774	9
+ 282	5
+ 39	3
+ 197	5
+ 91	3
+ 964	9
+ 546	5
+ 926	4
+ 332	1
+ 127	10
+ 15	4
+ 146	4
+ 376	4
+ 293	5
+ 396	2
+ 120	2
+ 83	4
+ 636	1
+ 677	8
+ 620	8
+ 128	6
+ 655	7
+ 84	6
+ 32	4
+ 651	2
+ 400	7
+ 510	5
+ 83	9
+ 957	4
+ 426	4
+ 554	5
+ 523	6
+ 949	2
+ 758	6
+ 992	4
+ 395	1
+ 962	0
+ 794	0
+ 630	8
+ 461	3
+ 984	9
+ 947	5
+ 408	0
+ 380	4
+ 407	8
+ 717	10
+ 352	2
+ 598	3
+ 399	4
+ 927	4
+ 734	3
+ 510	7
+ 371	3
+ 742	0
+ 129	2
+ 283	1
+ 63	2
+ 608	5
+ 261	10
+ 835	7
+ 793	6
+ 628	1
+ 793	2
+ 446	2
+ 582	4
+ 583	3
+ 695	1
+ 13	1
+ 397	8
+ 68	5
+ 957	4
+ 641	0
+ 582	2
+ 491	8
+ 235	3
+ 510	0
+ 879	1
+ 173	7
+ 365	6
+ 863	9
+ 992	4
+ 264	7
+ 540	3
+ 754	9
+ 32	8
+ 464	10
+ 174	1
+ 9	8
+ 353	5
+ 598	6
+ 827	1
+ 616	7
+ 247	8
+ 377	6
+ 407	2
+ 558	4
+ 686	8
+ 86	2
+ 99	8
+ 163	1
+ 662	6
+ 120	8
+ 731	1
+ 591	1
+ 630	2
+ 671	5
+ 298	3
+ 162	5
+ 75	5
+ 155	5
+ 779	7
+ 880	5
+ 535	10
+ 691	6
+ 806	9
+ 764	5
+ 480	9
+ 303	2
+ 13	9
+ 294	6
+ 84	10
+ 100	4
+ 252	3
+ 926	3
+ 801	1
+ 808	6
+ 794	7
+ 45	3
+ 655	7
+ 963	5
+ 589	7
+ 929	1
+ 611	2
+ 279	6
+ 127	6
+ 267	2
+ 538	4
+ 592	8
+ 629	5
+ 117	4
+ 599	9
+ 10	4
+ 614	1
+ 722	3
+ 790	7
+ 730	4
+ 413	7
+ 447	0
+ 891	7
+ 648	0
+ 299	9
+ 228	8
+ 282	8
+ 627	9
+ 338	7
+ 340	9
+ 669	3
+ 330	3
+ 404	1
+ 552	2
+ 738	3
+ 574	2
+ 941	0
+ 174	8
+ 747	8
+ 849	0
+ 738	1
+ 884	0
+ 897	5
+ 931	2
+ 256	3
+ 173	9
+ 621	5
+ 209	0
+ 556	8
+ 220	3
+ 43	8
+ 444	10
+ 815	6
+ 816	6
+ 441	7
+ 609	2
+ 742	5
+ 199	6
+ 4	1
+ 875	3
+ 400	0
+ 185	0
+ 551	4
+ 46	1
+ 155	3
+ 400	2
+ 60	8
+ 183	9
+ 463	10
+ 436	9
+ 665	0
+ 82	4
+ 538	3
+ 47	5
+ 410	9
+ 802	8
+ 970	10
+ 832	5
+ 381	9
+ 627	5
+ 145	0
+ 734	2
+ 872	9
+ 79	3
+ 916	5
+ 238	6
+ 560	3
+ 988	1
+ 602	0
+ 639	0
+ 956	4
+ 823	9
+ 429	7
+ 446	8
+ 533	1
+ 346	7
+ 101	1
+ 883	10
+ 997	10
+ 307	9
+ 477	5
+ 495	0
+ 865	5
+ 135	5
+ 517	8
+ 479	5
+ 215	3
+ 399	6
+ 957	8
+ 454	5
+ 919	8
+ 168	0
+ 880	1
+ 992	9
+ 13	3
+ 791	5
+ 844	3
+ 527	7
+ 768	7
+ 176	3
+ 435	7
+ 759	7
+ 957	2
+ 295	9
+ 4	7
+ 403	9
+ 548	6
+ 943	4
+ 622	9
+ 305	6
+ 235	1
+ 124	1
+ 381	7
+ 789	1
+ 312	10
+ 434	7
+ 619	2
+ 398	6
+ 351	7
+ 489	4
+ 442	9
+ 279	10
+ 463	2
+ 418	1
+ 158	7
+ 720	4
+ 819	8
+ 473	2
+ 496	3
+ 349	8
+ 226	8
+ 556	8
+ 976	10
+ 421	3
+ 648	9
+ 683	1
+ 803	10
+ 80	3
+ 184	5
+ 352	3
+ 221	1
+ 736	0
+ 917	2
+ 240	4
+ 470	6
+ 221	7
+ 372	8
+ 542	3
+ 731	10
+ 676	4
+ 874	4
+ 469	7
+ 321	5
+ 943	5
+ 46	3
+ 848	3
+ 367	6
+ 307	3
+ 793	5
+ 697	3
+ 135	9
+ 959	5
+ 695	5
+ 855	4
+ 464	5
+ 806	3
+ 890	3
+ 14	2
+ 822	10
+ 715	9
+ 253	6
+ 135	6
+ 147	4
+ 904	9
+ 988	6
+ 203	1
+ 519	2
+ 630	2
+ 663	5
+ 640	1
+ 16	4
+ 465	9
+ 720	5
+ 115	5
+ 437	8
+ 410	7
+ 393	5
+ 309	5
+ 987	2
+ 479	10
+ 814	7
+ 97	3
+ 844	7
+ 547	5
+ 212	2
+ 634	2
+ 634	1
+ 133	4
+ 579	2
+ 896	0
+ 79	3
+ 706	5
+ 852	0
+ 12	8
+ 228	5
+ 813	0
+ 173	9
+ 376	0
+ 637	9
+ 524	8
+ 111	2
+ 76	7
+ 258	2
+ 98	8
+ 457	10
+ 853	5
+ 301	6
+ 8	2
+ 574	0
+ 991	8
+ 511	8
+ 845	7
+ 713	2
+ 702	4
+ 144	2
+ 199	3
+ 385	3
+ 999	6
+ 483	1
+ 481	9
+ 91	3
+ 475	4
+ 893	5
+ 544	5
+ 503	5
+ 270	0
+ 338	1
+ 698	1
+ 336	4
+ 402	5
+ 626	6
+ 735	0
+ 875	7
+ 655	4
+ 830	1
+ 298	9
+ 469	8
+ 313	4
+ 256	9
+ 830	8
+ 392	1
+ 773	7
+ 215	5
+ 782	6
+ 871	2
+ 31	5
+ 784	8
+ 509	7
+ 499	2
+ 17	3
+ 299	3
+ 250	8
+ 89	6
+ 130	3
+ 421	10
+ 104	8
+ 59	9
+ 543	3
+ 348	3
+ 824	2
+ 508	9
+ 717	3
+ 620	2
+ 950	1
+ 390	10
+ 448	7
+ 282	7
+ 457	4
+ 262	6
+ 716	7
+ 546	8
+ 496	6
+ 697	0
+ 879	0
+ 363	7
+ 265	9
+ 557	10
+ 163	2
+ 209	1
+ 296	6
+ 80	7
+ 288	4
+ 442	7
+ 733	7
+ 332	4
+ 387	9
+ 269	9
+ 483	10
+ 921	4
+ 12	3
+ 64	3
+ 155	6
+ 260	3
+ 799	5
+ 431	1
+ 68	5
+ 839	4
+ 873	3
+ 101	6
+ 986	4
+ 55	4
+ 311	3
+ 255	8
+ 290	2
+ 155	3
+ 460	2
+ 579	6
+ 840	8
+ 933	6
+ 308	4
+ 735	4
+ 875	6
+ 733	7
+ 855	8
+ 353	8
+ 268	4
+ 213	6
+ 732	5
+ 372	0
+ 644	5
+ 324	1
+ 746	9
+ 718	6
+ 743	7
+ 225	1
+ 15	10
+ 428	9
+ 534	2
+ 637	4
+ 996	10
+ 230	3
+ 399	4
+ 842	1
+ 911	2
+ 153	6
+ 741	5
+ 658	5
+ 380	4
+ 72	1
+ 28	3
+ 174	0
+ 258	6
+ 933	8
+ 763	6
+ 181	8
+ 561	4
+ 22	10
+ 854	9
+ 90	8
+ 78	2
+ 320	8
+ 719	10
+ 305	1
+ 354	4
+ 222	4
+ 675	4
+ 425	9
+ 997	4
+ 725	8
+ 928	9
+ 518	5
+ 317	5
+ 447	2
+ 405	5
+ 936	5
+ 780	3
+ 302	5
+ 233	6
+ 598	6
+ 985	8
+ 969	7
+ 215	4
+ 594	2
+ 752	3
+ 973	7
+ 224	5
+ 167	5
+ 32	6
+ 712	4
+ 152	6
+ 920	9
+ 903	2
+ 430	1
+ 830	0
+ 724	8
+ 848	7
+ 477	1
+ 88	1
+ 276	8
+ 389	2
+ 519	6
+ 740	7
+ 154	8
+ 301	9
+ 209	5
+ 514	1
+ 385	4
+ 351	8
+ 553	2
+ 843	3
+ 998	7
+ 971	5
+ 754	1
+ 545	0
+ 898	9
+ 279	4
+ 547	0
+ 104	7
+ 791	4
+ 568	10
+ 858	1
+ 129	2
+ 499	5
+ 58	1
+ 662	9
+ 330	7
+ 592	3
+ 134	3
+ 359	7
+ 376	3
+ 613	7
+ 675	2
+ 674	8
+ 862	5
+ 183	4
+ 465	0
+ 512	6
+ 284	0
+ 74	3
+ 63	7
+ 244	4
+ 395	8
+ 693	5
+ 181	1
+ 208	6
+ 309	8
+ 212	10
+ 981	9
+ 763	8
+ 352	9
+ 273	8
+ 988	8
+ 410	3
+ 796	5
+ 614	9
+ 220	9
+ 251	6
+ 693	9
+ 144	9
+ 995	4
+ 432	3
+ 174	6
+ 289	2
+ 531	1
+ 998	9
+ 997	3
+ 700	10
+ 57	1
+ 257	9
+ 594	9
+ 711	8
+ 730	10
+ 429	4
+ 905	6
+ 298	9
+ 926	7
+ 205	1
+ 374	5
+ 254	9
+ 545	3
+ 788	5
+ 524	5
+ 528	6
+ 598	8
+ 433	2
+ 656	1
+ 6	4
+ 105	4
+ 809	0
+ 8	1
+ 910	9
+ 836	1
+ 34	2
+ 608	3
+ 115	2
+ 541	9
+ 696	1
+ 391	2
+ 645	10
+ 8	1
+ 180	7
+ 220	2
+ 51	3
+ 621	9
+ 335	6
+ 966	2
+ 564	8
+ 359	6
+ 12	10
+ 887	1
+ 120	4
+ 31	8
+ 492	4
+ 40	1
+ 410	0
+ 213	6
+ 713	4
+ 777	8
+ 759	4
+ 623	1
+ 28	6
+ 338	6
+ 390	7
+ 191	4
+ 663	1
+ 530	8
+ 505	6
+ 599	10
+ 983	6
+ 133	4
+ 687	3
+ 984	4
+ 780	8
+ 163	5
+ 160	8
+ 632	2
+ 374	10
+ 780	8
+ 666	10
+ 167	3
+ 48	7
+ 112	6
+ 258	7
+ 549	2
+ 350	7
+ 635	0
+ 27	6
+ 437	8
+ 380	6
+ 345	5
+ 386	10
+ 727	8
+ 947	5
+ 525	6
+ 477	7
+ 942	5
+ 389	1
+ 77	6
+ 765	6
+ 889	1
+ 308	5
+ 153	3
+ 142	6
+ 143	5
+ 191	5
+ 62	6
+ 465	8
+ 338	4
+ 296	9
+ 25	8
+ 555	10
+ 298	9
+ 20	4
+ 591	8
+ 2	5
+ 901	3
+ 3	1
+ 645	1
+ 645	8
+ 667	8
+ 276	7
+ 413	7
+ 517	8
+ 153	8
+ 613	2
+ 586	2
+ 144	9
+ 112	2
+ 259	7
+ 949	3
+ 183	9
+ 570	2
+ 904	2
+ 331	5
+ 3	10
+ 385	3
+ 726	8
+ 20	2
+ 549	2
+ 56	2
+ 351	6
+ 330	5
+ 525	4
+ 658	8
+ 144	6
+ 45	3
+ 458	6
+ 513	4
+ 830	8
+ 911	8
+ 841	3
+ 112	6
+ 94	1
+ 810	6
+ 305	9
+ 806	7
+ 508	1
+ 150	0
+ 577	8
+ 817	7
+ 416	9
+ 49	9
+ 477	6
+ 236	3
+ 405	1
+ 140	2
+ 443	3
+ 812	5
+ 385	6
+ 181	7
+ 489	10
+ 345	10
+ 122	5
+ 30	7
+ 304	8
+ 421	7
+ 710	5
+ 594	2
+ 31	8
+ 493	4
+ 977	6
+ 681	4
+ 886	5
+ 958	3
+ 116	1
+ 960	6
+ 126	3
+ 602	2
+ 801	6
+ 948	1
+ 480	4
+ 825	2
+ 840	4
+ 376	9
+ 249	9
+ 307	2
+ 504	10
+ 646	4
+ 482	6
+ 661	6
+ 744	6
+ 203	9
+ 927	8
+ 118	7
+ 438	1
+ 833	9
+ 436	7
+ 108	3
+ 77	5
+ 147	3
+ 354	5
+ 552	9
+ 443	2
+ 248	9
+ 802	9
+ 523	5
+ 530	7
+ 416	5
+ 532	5
+ 186	10
+ 600	0
+ 887	0
+ 677	10
+ 312	8
+ 479	5
+ 80	8
+ 913	6
+ 691	4
+ 830	9
+ 281	6
+ 848	8
+ 178	4
+ 530	6
+ 835	1
+ 128	0
+ 31	7
+ 39	9
+ 765	7
+ 914	1
+ 470	4
+ 537	6
+ 226	4
+ 183	9
+ 806	0
+ 855	1
+ 645	7
+ 890	8
+ 81	4
+ 418	9
+ 482	5
+ 937	5
+ 274	10
+ 432	0
+ 692	3
+ 116	2
+ 738	7
+ 713	10
+ 102	9
+ 881	9
+ 909	7
+ 994	6
+ 439	9
+ 378	5
+ 304	8
+ 436	8
+ 341	4
+ 299	6
+ 349	7
+ 653	0
+ 76	8
+ 203	8
+ 421	9
+ 778	5
+ 812	7
+ 431	7
+ 395	4
+ 275	8
+ 309	7
+ 354	6
+ 449	8
+ 398	8
+ 163	7
+ 405	5
+ 428	1
+ 552	5
+ 828	8
+ 319	2
+ 672	1
+ 772	5
+ 756	2
+ 205	2
+ 628	5
+ 986	9
+ 134	3
+ 550	6
+ 130	9
+ 373	3
+ 644	8
+ 805	1
+ 837	4
+ 576	7
+ 113	9
+ 913	8
+ 992	7
+ 270	7
+ 889	5
+ 899	5
+ 956	9
+ 455	1
+ 225	0
+ 673	4
+ 952	0
+ 648	6
+ 825	5
+ 669	7
+ 811	2
+ 326	9
+ 140	2
+ 710	1
+ 925	10
+ 881	8
+ 454	8
+ 331	4
+ 665	8
+ 500	9
+ 791	2
+ 245	7
+ 219	9
+ 339	0
+ 347	0
+ 705	2
+ 253	0
+ 82	4
+ 270	8
+ 526	2
+ 771	4
+ 8	2
+ 186	3
+ 635	9
+ 126	1
+ 741	9
+ 307	10
+ 659	5
+ 878	10
+ 570	2
+ 5	3
+ 383	3
+ 306	5
+ 651	6
+ 256	2
+ 769	0
+ 583	8
+ 251	8
+ 117	9
+ 620	2
+ 21	4
+ 158	3
+ 346	8
+ 854	2
+ 814	4
+ 449	8
+ 699	8
+ 78	0
+ 296	7
+ 580	6
+ 905	3
+ 578	5
+ 127	8
+ 257	2
+ 715	9
+ 486	7
+ 237	6
+ 64	6
+ 461	9
+ 808	3
+ 342	3
+ 95	0
+ 89	2
+ 47	4
+ 901	6
+ 937	8
+ 977	5
+ 294	1
+ 344	6
+ 348	1
+ 428	8
+ 795	7
+ 478	9
+ 249	9
+ 777	1
+ 215	1
+ 314	3
+ 161	4
+ 482	2
+ 787	4
+ 835	7
+ 190	8
+ 238	5
+ 917	6
+ 36	3
+ 641	5
+ 100	4
+ 130	6
+ 295	4
+ 517	1
+ 436	7
+ 191	7
+ 42	4
+ 152	5
+ 559	9
+ 908	4
+ 663	1
+ 207	9
+ 583	1
+ 483	6
+ 390	1
+ 84	5
+ 561	2
+ 67	9
+ 593	6
+ 928	0
+ 316	1
+ 780	4
+ 470	9
+ 882	0
+ 871	8
+ 424	5
+ 888	6
+ 434	5
+ 756	9
+ 90	1
+ 42	2
+ 636	6
+ 387	7
+ 459	10
+ 288	4
+ 11	6
+ 505	8
+ 962	10
+ 722	8
+ 4	6
+ 634	4
+ 125	5
+ 59	6
+ 994	8
+ 476	1
+ 962	5
+ 257	6
+ 121	6
+ 301	6
+ 625	6
+ 966	6
+ 193	5
+ 426	2
+ 445	1
+ 1000	4
+ 739	6
+ 876	9
+ 157	9
+ 424	2
+ 751	9
+ 234	7
+ 418	5
+ 310	5
+ 135	6
+ 118	8
+ 200	1
+ 396	4
+ 555	8
+ 548	0
+ 969	5
+ 449	7
+ 183	3
+ 572	3
+ 261	10
+ 490	0
+ 896	7
+ 724	3
+ 214	0
+ 853	3
+ 645	10
+ 109	8
+ 56	5
+ 237	6
+ 326	8
+ 611	3
+ 334	1
+ 3	5
+ 385	6
+ 856	6
+ 571	3
+ 658	5
+ 69	4
+ 782	3
+ 415	6
+ 633	1
+ 607	7
+ 904	7
+ 248	1
+ 274	6
+ 927	9
+ 869	3
+ 945	9
+ 777	3
+ 447	6
+ 977	0
+ 978	6
+ 485	0
+ 16	3
+ 331	4
+ 902	10
+ 491	5
+ 707	4
+ 172	10
+ 537	4
+ 528	5
+ 331	4
+ 724	3
+ 268	5
+ 607	7
+ 134	6
+ 733	1
+ 219	2
+ 159	2
+ 485	5
+ 666	4
+ 455	2
+ 897	2
+ 552	1
+ 116	1
+ 515	6
+ 552	8
+ 42	3
+ 123	3
+ 777	7
+ 25	9
+ 314	8
+ 23	5
+ 975	2
+ 767	5
+ 673	4
+ 849	1
+ 591	7
+ 290	1
+ 815	4
+ 232	3
+ 51	8
+ 176	1
+ 61	3
+ 403	8
+ 28	4
+ 748	3
+ 185	8
+ 875	2
+ 953	6
+ 621	6
+ 76	5
+ 754	7
+ 216	0
+ 810	0
+ 451	0
+ 360	5
+ 826	5
+ 596	9
+ 834	10
+ 724	9
+ 426	5
+ 205	6
+ 244	1
+ 771	2
+ 724	4
+ 823	8
+ 863	6
+ 466	1
+ 622	3
+ 109	1
+ 318	5
+ 576	1
+ 6	2
+ 30	8
+ 170	8
+ 702	6
+ 226	9
+ 207	5
+ 989	10
+ 667	7
+ 372	5
+ 512	2
+ 67	10
+ 313	7
+ 254	4
+ 762	6
+ 892	3
+ 715	9
+ 510	7
+ 738	7
+ 498	4
+ 276	7
+ 348	5
+ 194	3
+ 462	9
+ 49	8
+ 350	6
+ 68	4
+ 539	4
+ 106	8
+ 804	9
+ 365	7
+ 207	1
+ 595	7
+ 824	3
+ 397	3
+ 773	7
+ 47	1
+ 156	2
+ 457	6
+ 101	5
+ 452	5
+ 66	5
+ 869	6
+ 902	10
+ 397	7
+ 844	8
+ 403	1
+ 841	10
+ 768	7
+ 330	2
+ 988	1
+ 837	0
+ 223	10
+ 276	7
+ 611	4
+ 185	1
+ 829	3
+ 583	7
+ 855	5
+ 672	3
+ 190	5
+ 14	6
+ 567	9
+ 590	3
+ 521	9
+ 498	5
+ 22	3
+ 544	2
+ 328	8
+ 925	9
+ 197	1
+ 1	0
+ 361	6
+ 723	2
+ 68	4
+ 469	3
+ 911	5
+ 851	5
+ 338	4
+ 812	9
+ 361	3
+ 368	4
+ 645	9
+ 629	10
+ 732	6
+ 911	9
+ 663	9
+ 955	0
+ 495	7
+ 241	6
+ 74	7
+ 820	10
+ 192	7
+ 462	5
+ 112	3
+ 388	5
+ 584	8
+ 856	2
+ 667	5
+ 201	4
+ 38	1
+ 329	7
+ 24	3
+ 726	5
+ 963	10
+ 81	0
+ 676	9
+ 21	9
+ 573	5
+ 398	7
+ 757	8
+ 157	3
+ 542	0
+ 569	2
+ 498	8
+ 608	5
+ 882	9
+ 238	9
+ 221	10
+ 424	2
+ 931	5
+ 221	6
+ 407	2
+ 476	10
+ 725	9
+ 664	5
+ 660	8
+ 822	2
+ 835	4
+ 411	3
+ 160	0
+ 870	0
+ 956	1
+ 947	2
+ 73	4
+ 362	0
+ 877	6
+ 612	3
+ 824	1
+ 265	5
+ 963	9
+ 31	6
+ 751	9
+ 825	6
+ 243	2
+ 920	4
+ 256	8
+ 445	2
+ 898	4
+ 390	10
+ 764	8
+ 975	6
+ 335	6
+ 926	2
+ 675	2
+ 708	6
+ 121	7
+ 261	9
+ 592	1
+ 458	8
+ 323	4
+ 238	6
+ 167	7
+ 791	1
+ 75	2
+ 36	8
+ 933	0
+ 481	3
+ 597	4
+ 427	3
+ 598	1
+ 910	7
+ 874	2
+ 590	5
+ 258	0
+ 300	6
+ 425	5
+ 160	6
+ 221	10
+ 657	3
+ 131	7
+ 134	1
+ 703	6
+ 332	3
+ 22	8
+ 573	6
+ 894	5
+ 339	8
+ 655	9
+ 234	9
+ 978	5
+ 494	4
+ 73	7
+ 995	3
+ 603	7
+ 588	7
+ 345	7
+ 799	0
+ 338	1
+ 349	4
+ 889	9
+ 980	8
+ 404	3
+ 551	1
+ 249	8
+ 972	2
+ 319	5
+ 629	4
+ 118	6
+ 685	7
+ 277	3
+ 456	6
+ 996	3
+ 670	3
+ 385	0
+ 694	3
+ 940	7
+ 57	3
+ 993	6
+ 404	2
+ 392	4
+ 468	7
+ 840	1
+ 103	10
+ 721	8
+ 680	10
+ 61	1
+ 620	1
+ 392	3
+ 391	8
+ 310	1
+ 51	3
+ 759	1
+ 595	8
+ 716	10
+ 993	1
+ 374	5
+ 819	2
+ 558	9
+ 172	3
+ 710	9
+ 278	8
+ 989	9
+ 829	4
+ 188	2
+ 158	5
+ 305	2
+ 748	1
+ 317	3
+ 815	0
+ 341	8
+ 141	7
+ 270	10
+ 929	8
+ 883	1
+ 108	6
+ 954	4
+ 364	9
+ 283	2
+ 324	5
+ 413	5
+ 970	7
+ 691	7
+ 781	0
+ 61	6
+ 41	4
+ 405	2
+ 118	7
+ 142	0
+ 504	0
+ 148	6
+ 618	1
+ 997	10
+ 46	3
+ 175	4
+ 752	6
+ 852	7
+ 306	5
+ 440	1
+ 552	5
+ 684	6
+ 904	1
+ 775	0
+ 764	9
+ 68	3
+ 942	2
+ 880	6
+ 319	9
+ 542	4
+ 157	7
+ 734	9
+ 306	6
+ 632	6
+ 130	1
+ 699	7
+ 574	4
+ 275	5
+ 472	1
+ 500	2
+ 968	6
+ 505	9
+ 785	4
+ 470	1
+ 261	0
+ 468	4
+ 730	2
+ 328	0
+ 788	10
+ 648	9
+ 32	3
+ 601	6
+ 730	9
+ 83	2
+ 926	6
+ 439	9
+ 151	9
+ 803	9
+ 327	3
+ 39	6
+ 285	5
+ 7	0
+ 709	3
+ 52	5
+ 294	7
+ 416	3
+ 47	0
+ 931	8
+ 892	0
+ 979	8
+ 597	4
+ 712	7
+ 361	5
+ 685	7
+ 789	7
+ 277	1
+ 231	3
+ 89	9
+ 618	1
+ 437	9
+ 840	9
+ 237	9
+ 869	2
+ 664	8
+ 181	6
+ 580	8
+ 61	3
+ 527	4
+ 808	2
+ 110	6
+ 936	4
+ 671	2
+ 670	8
+ 106	3
+ 901	5
+ 199	7
+ 395	4
+ 629	3
+ 603	3
+ 26	8
+ 936	6
+ 562	10
+ 898	1
+ 418	7
+ 301	5
+ 303	2
+ 915	10
+ 403	6
+ 733	5
+ 873	6
+ 52	1
+ 376	4
+ 508	0
+ 712	1
+ 297	7
+ 894	2
+ 344	5
+ 229	2
+ 546	6
+ 948	8
+ 176	3
+ 84	1
+ 225	5
+ 677	10
+ 996	5
+ 592	0
+ 622	10
+ 495	1
+ 972	2
+ 240	3
+ 944	1
+ 502	3
+ 591	7
+ 530	1
+ 379	5
+ 984	6
+ 730	1
+ 646	10
+ 555	3
+ 912	6
+ 873	5
+ 598	5
+ 472	1
+ 625	4
+ 299	9
+ 713	2
+ 1000	2
+ 531	6
+ 946	1
+ 728	3
+ 540	7
+ 881	3
+ 781	5
+ 223	3
+ 850	1
+ 885	7
+ 639	5
+ 218	1
+ 576	8
+ 555	9
+ 707	3
+ 120	7
+ 483	7
+ 298	4
+ 712	0
+ 755	3
+ 739	6
+ 521	5
+ 162	7
+ 854	0
+ 880	7
+ 735	5
+ 223	10
+ 630	8
+ 795	2
+ 676	5
+ 454	8
+ 208	9
+ 446	5
+ 367	2
+ 532	1
+ 410	3
+ 757	9
+ 789	9
+ 676	6
+ 931	6
+ 384	7
+ 74	6
+ 618	7
+ 407	4
+ 890	1
+ 915	3
+ 878	1
+ 281	3
+ 629	6
+ 482	2
+ 769	9
+ 431	5
+ 824	2
+ 445	5
+ 865	4
+ 55	2
+ 42	1
+ 855	7
+ 834	3
+ 73	7
+ 344	10
+ 68	2
+ 111	3
+ 545	7
+ 996	0
+ 901	8
+ 920	3
+ 291	7
+ 553	7
+ 243	4
+ 111	3
+ 666	2
+ 427	5
+ 812	3
+ 783	9
+ 985	1
+ 873	1
+ 348	10
+ 401	9
+ 724	4
+ 921	6
+ 163	8
+ 957	5
+ 584	5
+ 189	8
+ 928	3
+ 125	6
+ 452	6
+ 114	3
+ 814	9
+ 150	8
+ 23	0
+ 851	4
+ 7	3
+ 265	7
+ 650	2
+ 356	8
+ 25	3
+ 266	6
+ 824	5
+ 436	8
+ 754	6
+ 345	2
+ 114	5
+ 471	9
+ 356	6
+ 726	4
+ 644	6
+ 751	7
+ 829	0
+ 383	5
+ 201	7
+ 291	2
+ 53	6
+ 836	9
+ 11	3
+ 628	8
+ 834	10
+ 972	9
+ 433	4
+ 875	8
+ 63	6
+ 170	7
+ 178	9
+ 359	0
+ 937	7
+ 487	1
+ 481	8
+ 365	5
+ 335	2
+ 411	3
+ 472	0
+ 112	3
+ 13	1
+ 254	4
+ 526	1
+ 236	6
+ 730	4
+ 297	9
+ 327	7
+ 916	3
+ 399	4
+ 402	9
+ 181	8
+ 414	5
+ 967	8
+ 862	4
+ 865	10
+ 745	9
+ 58	10
+ 325	6
+ 128	6
+ 173	9
+ 967	5
+ 767	3
+ 127	7
+ 558	5
+ 86	10
+ 405	3
+ 726	8
+ 783	7
+ 645	6
+ 132	5
+ 619	9
+ 388	7
+ 876	7
+ 260	0
+ 273	4
+ 862	2
+ 903	6
+ 534	0
+ 312	1
+ 555	4
+ 51	10
+ 665	8
+ 780	4
+ 469	4
+ 93	6
+ 934	7
+ 477	3
+ 388	4
+ 34	6
+ 356	3
+ 81	2
+ 546	10
+ 847	1
+ 15	2
+ 171	6
+ 558	2
+ 531	2
+ 998	3
+ 672	5
+ 735	8
+ 67	7
+ 476	5
+ 991	9
+ 897	0
+ 512	3
+ 332	6
+ 471	9
+ 578	3
+ 958	6
+ 477	1
+ 163	0
+ 351	7
+ 259	3
+ 4	9
+ 816	7
+ 695	9
+ 409	2
+ 427	4
+ 35	3
+ 426	5
+ 576	8
+ 140	0
+ 636	7
+ 365	6
+ 311	8
+ 724	5
+ 877	1
+ 167	1
+ 424	2
+ 67	2
+ 911	8
+ 122	3
+ 932	5
+ 721	10
+ 872	1
+ 514	4
+ 905	7
+ 495	5
+ 372	9
+ 135	7
+ 702	9
+ 156	6
+ 933	3
+ 715	4
+ 495	8
+ 596	4
+ 543	7
+ 726	5
+ 267	4
+ 442	1
+ 595	10
+ 588	5
+ 610	1
+ 40	10
+ 943	2
+ 665	6
+ 34	8
+ 224	10
+ 145	9
+ 324	6
+ 721	9
+ 45	3
+ 638	8
+ 739	9
+ 219	2
+ 45	8
+ 138	6
+ 314	7
+ 717	4
+ 730	7
+ 529	4
+ 305	6
+ 216	5
+ 531	4
+ 468	9
+ 0	2
+ 776	0
+ 453	4
+ 817	2
+ 320	0
+ 373	4
+ 850	5
+ 998	2
+ 259	7
+ 518	10
+ 375	0
+ 384	7
+ 611	6
+ 209	1
+ 961	7
+ 998	10
+ 866	8
+ 7	3
+ 188	8
+ 511	5
+ 861	9
+ 873	7
+ 395	9
+ 875	7
+ 586	4
+ 643	10
+ 441	0
+ 642	1
+ 628	9
+ 195	6
+ 529	2
+ 551	4
+ 967	6
+ 714	2
+ 383	2
+ 663	2
+ 109	5
+ 955	5
+ 408	8
+ 158	10
+ 223	8
+ 956	7
+ 829	6
+ 717	5
+ 449	9
+ 46	10
+ 104	6
+ 373	1
+ 156	1
+ 226	5
+ 313	9
+ 781	4
+ 426	7
+ 926	8
+ 566	1
+ 830	8
+ 886	8
+ 454	7
+ 384	2
+ 172	8
+ 82	2
+ 811	2
+ 816	2
+ 257	10
+ 272	5
+ 509	6
+ 373	3
+ 6	8
+ 27	9
+ 635	6
+ 16	5
+ 382	9
+ 250	8
+ 617	6
+ 7	8
+ 468	1
+ 7	3
+ 275	8
+ 464	5
+ 794	7
+ 16	3
+ 320	4
+ 593	3
+ 189	6
+ 259	8
+ 213	3
+ 288	6
+ 177	5
+ 431	8
+ 173	4
+ 583	6
+ 527	6
+ 920	8
+ 413	4
+ 335	2
+ 120	4
+ 509	4
+ 740	1
+ 767	9
+ 722	0
+ 754	9
+ 301	0
+ 530	5
+ 581	10
+ 273	8
+ 400	9
+ 395	9
+ 446	3
+ 728	9
+ 700	1
+ 65	8
+ 414	6
+ 260	2
+ 676	0
+ 84	4
+ 52	8
+ 334	4
+ 880	9
+ 832	5
+ 826	1
+ 216	2
+ 960	6
+ 152	4
+ 927	9
+ 265	6
+ 943	3
+ 446	4
+ 904	7
+ 511	6
+ 733	6
+ 979	8
+ 433	3
+ 138	3
+ 177	10
+ 775	0
+ 74	10
+ 227	0
+ 603	4
+ 441	5
+ 259	7
+ 157	2
+ 37	6
+ 559	9
+ 309	1
+ 522	0
+ 666	5
+ 827	1
+ 814	10
+ 413	10
+ 935	2
+ 993	0
+ 180	2
+ 44	8
+ 599	5
+ 312	9
+ 191	5
+ 61	2
+ 73	6
+ 169	4
+ 691	7
+ 424	4
+ 192	3
+ 456	0
+ 217	9
+ 997	2
+ 57	10
+ 162	2
+ 210	2
+ 19	8
+ 690	3
+ 668	9
+ 801	7
+ 109	9
+ 350	3
+ 256	0
+ 969	7
+ 399	2
+ 932	9
+ 168	1
+ 724	2
+ 301	8
+ 154	5
+ 19	4
+ 668	0
+ 173	4
+ 370	8
+ 239	2
+ 570	3
+ 45	9
+ 626	3
+ 962	6
+ 982	4
+ 757	9
+ 216	9
+ 63	9
+ 89	4
+ 722	2
+ 828	7
+ 606	5
+ 779	8
+ 854	1
+ 619	1
+ 320	2
+ 441	4
+ 110	1
+ 666	1
+ 663	6
+ 432	4
+ 562	6
+ 344	6
+ 588	4
+ 989	3
+ 676	8
+ 51	3
+ 313	8
+ 61	2
+ 978	7
+ 260	3
+ 870	7
+ 663	10
+ 768	3
+ 50	4
+ 978	5
+ 850	5
+ 129	2
+ 165	7
+ 628	2
+ 27	3
+ 971	1
+ 586	3
+ 907	6
+ 450	9
+ 327	7
+ 184	2
+ 410	8
+ 175	2
+ 177	2
+ 608	2
+ 707	5
+ 694	8
+ 652	9
+ 554	3
+ 13	6
+ 584	10
+ 658	2
+ 267	6
+ 816	7
+ 450	1
+ 428	6
+ 339	8
+ 480	5
+ 16	7
+ 739	6
+ 811	4
+ 82	5
+ 283	7
+ 364	8
+ 15	4
+ 417	6
+ 360	1
+ 769	6
+ 640	6
+ 345	1
+ 728	8
+ 723	1
+ 611	2
+ 581	6
+ 861	3
+ 252	7
+ 767	3
+ 177	1
+ 69	5
+ 887	1
+ 918	3
+ 684	3
+ 380	5
+ 906	0
+ 38	3
+ 110	8
+ 24	8
+ 833	6
+ 37	4
+ 263	9
+ 733	5
+ 570	5
+ 849	7
+ 550	9
+ 288	4
+ 2	2
+ 742	7
+ 484	1
+ 139	4
+ 142	2
+ 640	3
+ 942	7
+ 85	8
+ 300	1
+ 188	6
+ 20	9
+ 76	6
+ 422	9
+ 336	10
+ 843	6
+ 409	8
+ 830	2
+ 531	3
+ 274	7
+ 704	4
+ 846	3
+ 667	8
+ 8	8
+ 564	3
+ 874	8
+ 870	9
+ 674	9
+ 483	1
+ 871	8
+ 68	7
+ 444	5
+ 559	3
+ 629	1
+ 588	9
+ 760	3
+ 318	6
+ 636	10
+ 395	6
+ 737	10
+ 951	6
+ 711	8
+ 505	4
+ 767	10
+ 480	6
+ 807	5
+ 353	3
+ 25	9
+ 525	7
+ 2	1
+ 555	8
+ 405	9
+ 369	0
+ 858	8
+ 684	6
+ 723	6
+ 207	4
+ 456	7
+ 818	2
+ 701	3
+ 862	5
+ 845	2
+ 759	9
+ 127	3
+ 523	1
+ 397	1
+ 892	8
+ 952	3
+ 841	8
+ 25	5
+ 406	7
+ 160	6
+ 181	6
+ 325	10
+ 840	0
+ 297	7
+ 534	1
+ 916	3
+ 13	0
+ 577	5
+ 172	10
+ 615	1
+ 775	6
+ 325	6
+ 377	3
+ 141	8
+ 97	3
+ 396	3
+ 918	7
+ 278	8
+ 747	6
+ 459	3
+ 717	4
+ 574	7
+ 418	2
+ 265	6
+ 125	9
+ 655	9
+ 447	10
+ 516	8
+ 329	7
+ 606	4
+ 959	0
+ 705	9
+ 723	10
+ 634	5
+ 557	1
+ 751	3
+ 468	3
+ 4	9
+ 477	3
+ 477	6
+ 149	1
+ 501	6
+ 111	0
+ 419	4
+ 674	0
+ 867	6
+ 28	6
+ 509	8
+ 554	1
+ 221	1
+ 236	10
+ 385	7
+ 298	4
+ 590	8
+ 657	1
+ 375	8
+ 200	9
+ 402	3
+ 893	8
+ 752	6
+ 847	6
+ 199	9
+ 190	7
+ 626	7
+ 852	8
+ 854	1
+ 819	2
+ 792	1
+ 627	4
+ 891	3
+ 450	3
+ 91	6
+ 142	5
+ 961	0
+ 315	7
+ 602	2
+ 331	8
+ 37	5
+ 510	7
+ 265	4
+ 509	1
+ 450	3
+ 358	2
+ 445	10
+ 624	3
+ 270	1
+ 602	4
+ 724	7
+ 854	7
+ 779	2
+ 397	4
+ 331	7
+ 182	4
+ 248	7
+ 31	5
+ 55	5
+ 632	5
+ 869	10
+ 746	3
+ 976	4
+ 649	2
+ 445	3
+ 606	2
+ 995	5
+ 853	8
+ 629	2
+ 155	10
+ 977	3
+ 329	2
+ 30	4
+ 738	1
+ 901	4
+ 589	8
+ 361	3
+ 84	3
+ 706	7
+ 582	2
+ 984	2
+ 319	10
+ 648	2
+ 753	3
+ 421	9
+ 237	4
+ 246	6
+ 623	3
+ 926	4
+ 361	8
+ 732	9
+ 597	1
+ 285	7
+ 430	10
+ 414	0
+ 141	4
+ 201	5
+ 378	8
+ 631	1
+ 126	1
+ 40	4
+ 449	3
+ 929	1
+ 562	9
+ 433	9
+ 682	2
+ 872	3
+ 258	2
+ 960	7
+ 147	4
+ 700	3
+ 773	9
+ 747	2
+ 749	4
+ 282	9
+ 429	3
+ 239	9
+ 608	2
+ 949	2
+ 23	4
+ 92	7
+ 546	10
+ 984	8
+ 121	9
+ 491	3
+ 318	2
+ 556	1
+ 91	3
+ 242	8
+ 680	5
+ 716	1
+ 846	10
+ 986	5
+ 123	9
+ 624	1
+ 317	7
+ 851	9
+ 681	8
+ 668	8
+ 778	2
+ 71	1
+ 350	6
+ 186	4
+ 929	4
+ 282	6
+ 952	10
+ 718	8
+ 952	7
+ 252	1
+ 639	9
+ 221	10
+ 593	1
+ 820	3
+ 906	5
+ 76	7
+ 647	1
+ 779	10
+ 774	10
+ 438	7
+ 393	7
+ 312	3
+ 718	0
+ 143	7
+ 734	4
+ 745	4
+ 271	10
+ 330	9
+ 37	1
+ 138	9
+ 637	2
+ 627	3
+ 361	4
+ 281	1
+ 372	7
+ 838	8
+ 440	1
+ 110	2
+ 180	3
+ 828	9
+ 647	6
+ 287	9
+ 538	6
+ 782	6
+ 766	9
+ 518	4
+ 133	1
+ 688	5
+ 551	10
+ 629	9
+ 689	5
+ 688	1
+ 616	8
+ 287	8
+ 50	1
+ 709	7
+ 687	10
+ 616	2
+ 613	4
+ 801	4
+ 316	3
+ 782	4
+ 464	5
+ 944	0
+ 439	6
+ 939	1
+ 39	6
+ 257	7
+ 425	5
+ 451	5
+ 658	2
+ 173	3
+ 157	8
+ 570	8
+ 186	4
+ 148	5
+ 690	9
+ 951	2
+ 400	9
+ 170	8
+ 468	1
+ 967	5
+ 735	2
+ 162	2
+ 768	6
+ 635	4
+ 774	8
+ 771	9
+ 596	3
+ 700	8
+ 712	8
+ 283	4
+ 778	2
+ 556	2
+ 129	7
+ 17	6
+ 834	10
+ 104	6
+ 208	3
+ 729	10
+ 879	4
+ 403	7
+ 171	2
+ 583	8
+ 516	3
+ 548	2
+ 131	8
+ 631	9
+ 66	2
+ 86	2
+ 912	1
+ 792	7
+ 86	9
+ 315	3
+ 161	0
+ 272	0
+ 408	7
+ 693	6
+ 850	3
+ 346	4
+ 559	9
+ 595	7
+ 726	2
+ 598	8
+ 412	7
+ 987	3
+ 786	8
+ 70	9
+ 676	4
+ 167	8
+ 430	4
+ 877	8
+ 113	6
+ 417	10
+ 846	8
+ 330	4
+ 657	9
+ 94	4
+ 150	7
+ 175	6
+ 376	2
+ 886	2
+ 942	10
+ 34	6
+ 342	2
+ 455	8
+ 639	3
+ 610	8
+ 902	0
+ 715	7
+ 789	0
+ 152	4
+ 969	2
+ 829	1
+ 938	0
+ 682	3
+ 166	6
+ 475	1
+ 525	5
+ 725	9
+ 709	2
+ 639	3
+ 511	2
+ 99	4
+ 275	8
+ 160	1
+ 859	3
+ 509	8
+ 558	3
+ 948	5
+ 341	6
+ 809	5
+ 198	3
+ 614	7
+ 792	3
+ 590	5
+ 519	2
+ 848	0
+ 478	9
+ 443	8
+ 761	6
+ 816	6
+ 916	3
+ 448	5
+ 663	4
+ 970	0
+ 26	8
+ 512	2
+ 62	1
+ 947	9
+ 465	5
+ 354	10
+ 766	2
+ 14	2
+ 149	5
+ 996	9
+ 61	8
+ 530	10
+ 138	10
+ 451	8
+ 374	4
+ 806	4
+ 199	3
+ 623	3
+ 443	6
+ 115	9
+ 107	5
+ 893	9
+ 671	9
+ 117	8
+ 365	1
+ 730	4
+ 926	3
+ 403	1
+ 237	9
+ 865	6
+ 275	7
+ 10	5
+ 988	6
+ 736	4
+ 204	9
+ 340	3
+ 321	2
+ 185	10
+ 140	3
+ 812	5
+ 416	5
+ 931	3
+ 802	3
+ 405	0
+ 189	3
+ 650	5
+ 941	7
+ 939	9
+ 295	7
+ 361	5
+ 526	7
+ 810	8
+ 934	10
+ 839	1
+ 297	7
+ 579	7
+ 194	5
+ 54	10
+ 845	5
+ 36	0
+ 730	7
+ 498	7
+ 346	4
+ 602	6
+ 112	10
+ 140	6
+ 665	9
+ 486	6
+ 945	3
+ 673	2
+ 977	3
+ 954	2
+ 763	0
+ 167	6
+ 468	2
+ 641	2
+ 888	1
+ 870	2
+ 576	5
+ 876	7
+ 434	0
+ 326	1
+ 965	8
+ 698	9
+ 136	4
+ 151	1
+ 624	1
+ 284	4
+ 114	5
+ 994	6
+ 654	6
+ 780	5
+ 773	7
+ 777	3
+ 122	7
+ 36	6
+ 669	4
+ 655	6
+ 174	4
+ 544	3
+ 724	7
+ 423	3
+ 801	7
+ 734	9
+ 158	7
+ 497	8
+ 362	3
+ 354	1
+ 928	1
+ 484	0
+ 784	5
+ 605	5
+ 882	3
+ 87	1
+ 613	6
+ 365	3
+ 326	8
+ 685	1
+ 495	4
+ 42	7
+ 148	5
+ 465	5
+ 816	8
+ 646	7
+ 950	1
+ 793	7
+ 649	4
+ 187	5
+ 658	3
+ 587	3
+ 904	10
+ 608	2
+ 740	3
+ 356	2
+ 712	4
+ 888	9
+ 937	4
+ 370	8
+ 172	0
+ 497	1
+ 146	3
+ 855	8
+ 687	0
+ 327	3
+ 315	9
+ 616	2
+ 866	2
+ 449	6
+ 516	8
+ 842	2
+ 203	7
+ 89	1
+ 83	5
+ 893	3
+ 475	4
+ 376	6
+ 679	2
+ 416	4
+ 272	7
+ 711	6
+ 656	3
+ 806	5
+ 550	3
+ 129	1
+ 60	10
+ 295	3
+ 701	4
+ 403	8
+ 843	3
+ 39	3
+ 686	4
+ 940	4
+ 645	4
+ 732	9
+ 99	4
+ 504	8
+ 770	3
+ 278	3
+ 565	4
+ 386	6
+ 377	7
+ 887	1
+ 65	3
+ 861	9
+ 586	9
+ 227	3
+ 315	2
+ 638	10
+ 523	4
+ 878	6
+ 812	4
+ 378	6
+ 693	7
+ 901	3
+ 62	3
+ 881	4
+ 968	8
+ 517	0
+ 58	4
+ 941	6
+ 278	2
+ 917	6
+ 335	6
+ 553	9
+ 923	4
+ 480	7
+ 813	9
+ 316	5
+ 514	2
+ 763	6
+ 504	6
+ 16	5
+ 413	5
+ 505	5
+ 911	4
+ 116	2
+ 614	0
+ 782	9
+ 587	3
+ 806	5
+ 767	3
+ 246	6
+ 145	6
+ 86	7
+ 780	8
+ 236	3
+ 494	3
+ 756	9
+ 785	3
+ 378	7
+ 707	5
+ 885	3
+ 527	7
+ 269	1
+ 4	1
+ 625	8
+ 362	9
+ 351	5
+ 433	4
+ 166	2
+ 287	4
+ 497	8
+ 655	3
+ 688	4
+ 514	1
+ 137	2
+ 561	0
+ 541	1
+ 690	8
+ 202	7
+ 885	8
+ 464	2
+ 698	8
+ 753	1
+ 252	9
+ 345	5
+ 322	8
+ 321	10
+ 95	0
+ 418	6
+ 76	6
+ 829	6
+ 576	4
+ 725	3
+ 180	9
+ 959	1
+ 755	4
+ 311	5
+ 238	1
+ 585	5
+ 983	9
+ 30	3
+ 772	4
+ 283	9
+ 360	7
+ 476	4
+ 256	3
+ 73	8
+ 675	8
+ 98	9
+ 726	1
+ 919	5
+ 480	2
+ 934	7
+ 293	5
+ 208	3
+ 450	2
+ 582	2
+ 588	9
+ 88	9
+ 567	6
+ 384	8
+ 869	5
+ 655	5
+ 255	8
+ 398	10
+ 810	3
+ 461	3
+ 546	4
+ 8	8
+ 915	2
+ 115	4
+ 453	7
+ 586	0
+ 562	7
+ 988	1
+ 238	4
+ 952	1
+ 829	6
+ 650	1
+ 359	0
+ 64	2
+ 365	5
+ 459	9
+ 920	5
+ 749	8
+ 683	9
+ 200	1
+ 561	8
+ 176	1
+ 460	2
+ 252	7
+ 537	2
+ 805	4
+ 810	5
+ 449	2
+ 503	5
+ 338	9
+ 37	8
+ 778	10
+ 264	5
+ 793	9
+ 390	10
+ 83	10
+ 778	3
+ 74	2
+ 424	3
+ 936	10
+ 530	7
+ 326	3
+ 196	8
+ 509	7
+ 287	8
+ 567	3
+ 645	3
+ 282	9
+ 871	1
+ 856	3
+ 68	9
+ 212	8
+ 198	3
+ 84	6
+ 613	0
+ 583	1
+ 761	9
+ 484	10
+ 684	10
+ 657	10
+ 841	2
+ 296	5
+ 569	6
+ 395	4
+ 653	3
+ 702	7
+ 190	9
+ 567	4
+ 201	7
+ 11	8
+ 670	6
+ 957	4
+ 504	4
+ 389	2
+ 435	0
+ 160	3
+ 270	5
+ 761	8
+ 34	2
+ 279	7
+ 407	10
+ 408	6
+ 894	10
+ 986	1
+ 625	10
+ 908	3
+ 592	9
+ 727	1
+ 307	1
+ 285	7
+ 162	4
+ 17	4
+ 900	8
+ 270	9
+ 934	5
+ 622	3
+ 529	0
+ 939	4
+ 5	9
+ 518	6
+ 923	4
+ 925	5
+ 292	7
+ 612	6
+ 768	9
+ 341	9
+ 341	4
+ 362	2
+ 136	6
+ 175	1
+ 181	8
+ 411	7
+ 826	4
+ 134	8
+ 275	7
+ 461	2
+ 79	4
+ 714	4
+ 38	3
+ 970	8
+ 222	3
+ 737	6
+ 668	1
+ 803	8
+ 732	10
+ 873	9
+ 774	3
+ 623	6
+ 635	8
+ 431	9
+ 409	9
+ 108	5
+ 278	8
+ 858	3
+ 147	8
+ 123	4
+ 139	9
+ 931	8
+ 959	7
+ 611	7
+ 712	5
+ 604	5
+ 769	2
+ 86	4
+ 985	5
+ 313	4
+ 408	4
+ 881	7
+ 243	7
+ 2	4
+ 568	1
+ 760	7
+ 985	7
+ 514	9
+ 426	1
+ 636	1
+ 608	2
+ 624	4
+ 467	7
+ 780	5
+ 227	1
+ 846	6
+ 514	7
+ 321	8
+ 467	3
+ 148	0
+ 448	9
+ 742	4
+ 598	3
+ 378	0
+ 380	0
+ 162	10
+ 253	8
+ 365	7
+ 495	1
+ 173	7
+ 238	0
+ 356	8
+ 746	7
+ 510	2
+ 0	7
+ 248	4
+ 565	10
+ 882	2
+ 246	3
+ 187	6
+ 272	3
+ 614	5
+ 134	10
+ 246	6
+ 125	4
+ 350	4
+ 437	7
+ 115	2
+ 384	6
+ 396	4
+ 282	6
+ 833	8
+ 634	7
+ 10	9
+ 973	2
+ 507	2
+ 545	1
+ 771	7
+ 100	0
+ 307	2
+ 435	7
+ 588	9
+ 364	7
+ 55	7
+ 327	5
+ 132	6
+ 95	10
+ 455	7
+ 679	5
+ 609	7
+ 661	1
+ 897	2
+ 237	7
+ 885	3
+ 685	2
+ 563	1
+ 849	2
+ 991	2
+ 853	0
+ 961	2
+ 497	1
+ 788	6
+ 57	2
+ 320	7
+ 708	9
+ 387	4
+ 46	3
+ 575	3
+ 953	5
+ 620	6
+ 652	2
+ 758	5
+ 333	7
+ 714	2
+ 795	7
+ 365	3
+ 767	2
+ 883	8
+ 396	2
+ 559	1
+ 133	9
+ 472	2
+ 232	0
+ 461	2
+ 507	1
+ 823	2
+ 264	6
+ 659	6
+ 329	4
+ 783	1
+ 47	1
+ 416	8
+ 300	3
+ 638	7
+ 502	2
+ 800	6
+ 145	3
+ 814	4
+ 319	3
+ 561	8
+ 356	4
+ 984	6
+ 964	6
+ 218	3
+ 16	0
+ 418	1
+ 148	8
+ 878	4
+ 133	5
+ 144	6
+ 714	9
+ 270	9
+ 217	1
+ 235	5
+ 358	8
+ 362	7
+ 180	3
+ 335	1
+ 989	6
+ 437	0
+ 553	9
+ 69	7
+ 689	9
+ 149	8
+ 463	3
+ 457	2
+ 238	7
+ 36	5
+ 810	3
+ 990	2
+ 67	4
+ 883	2
+ 698	2
+ 390	7
+ 771	8
+ 693	3
+ 683	8
+ 26	4
+ 709	2
+ 194	2
+ 469	7
+ 349	7
+ 377	4
+ 161	2
+ 656	2
+ 355	7
+ 503	2
+ 969	2
+ 456	4
+ 889	2
+ 187	6
+ 553	9
+ 344	6
+ 241	1
+ 754	4
+ 225	2
+ 85	6
+ 929	5
+ 960	1
+ 650	6
+ 241	0
+ 339	7
+ 244	3
+ 946	7
+ 668	8
+ 928	9
+ 418	5
+ 725	8
+ 59	10
+ 815	8
+ 400	0
+ 35	5
+ 614	10
+ 948	6
+ 53	6
+ 190	3
+ 604	5
+ 39	8
+ 838	10
+ 547	5
+ 820	5
+ 361	2
+ 955	1
+ 0	0
+ 52	8
+ 826	5
+ 855	9
+ 938	5
+ 825	9
+ 43	9
+ 484	2
+ 173	1
+ 762	2
+ 935	6
+ 196	5
+ 107	0
+ 957	5
+ 254	9
+ 554	3
+ 926	6
+ 69	8
+ 58	9
+ 614	10
+ 393	4
+ 882	4
+ 317	4
+ 669	5
+ 454	4
+ 701	4
+ 32	9
+ 871	1
+ 913	8
+ 607	2
+ 740	2
+ 421	7
+ 767	5
+ 418	8
+ 414	0
+ 821	8
+ 470	7
+ 244	8
+ 69	9
+ 277	5
+ 345	10
+ 912	4
+ 875	8
+ 516	8
+ 611	1
+ 956	4
+ 285	4
+ 16	1
+ 867	4
+ 878	3
+ 466	7
+ 88	9
+ 401	3
+ 723	5
+ 245	0
+ 992	6
+ 978	9
+ 967	9
+ 687	5
+ 642	3
+ 607	6
+ 648	9
+ 974	7
+ 944	8
+ 98	8
+ 122	6
+ 520	2
+ 500	9
+ 541	2
+ 391	8
+ 223	4
+ 376	2
+ 288	3
+ 55	10
+ 827	7
+ 273	4
+ 294	9
+ 325	3
+ 585	3
+ 108	7
+ 92	2
+ 248	6
+ 440	7
+ 533	10
+ 971	9
+ 767	2
+ 308	1
+ 395	6
+ 487	4
+ 571	3
+ 146	8
+ 747	4
+ 765	1
+ 707	4
+ 342	8
+ 34	4
+ 46	3
+ 46	5
+ 30	6
+ 466	0
+ 504	2
+ 194	8
+ 378	6
+ 408	9
+ 38	10
+ 178	2
+ 823	9
+ 624	6
+ 997	3
+ 939	3
+ 147	10
+ 772	2
+ 255	8
+ 677	3
+ 397	1
+ 286	9
+ 378	5
+ 712	8
+ 69	1
+ 620	1
+ 98	8
+ 291	9
+ 722	9
+ 509	7
+ 245	4
+ 58	4
+ 421	8
+ 584	7
+ 648	3
+ 962	0
+ 405	2
+ 945	8
+ 727	7
+ 538	8
+ 776	2
+ 903	9
+ 956	2
+ 796	7
+ 108	3
+ 397	4
+ 753	5
+ 745	2
+ 285	3
+ 850	9
+ 591	8
+ 977	10
+ 59	9
+ 780	8
+ 578	3
+ 583	4
+ 476	5
+ 228	4
+ 679	0
+ 110	8
+ 329	5
+ 141	1
+ 963	9
+ 255	2
+ 215	1
+ 181	8
+ 917	2
+ 803	10
+ 80	6
+ 763	7
+ 900	3
+ 12	4
+ 831	2
+ 809	5
+ 264	9
+ 297	6
+ 427	4
+ 674	4
+ 324	9
+ 638	5
+ 34	8
+ 346	10
+ 978	1
+ 928	1
+ 730	7
+ 716	6
+ 36	7
+ 7	9
+ 969	8
+ 378	2
+ 735	7
+ 826	2
+ 113	5
+ 552	4
+ 429	2
+ 976	5
+ 10	3
+ 415	10
+ 470	3
+ 46	2
+ 33	8
+ 831	1
+ 490	8
+ 937	5
+ 654	3
+ 691	4
+ 990	5
+ 550	1
+ 17	1
+ 539	4
+ 292	5
+ 909	3
+ 837	3
+ 289	3
+ 666	3
+ 507	7
+ 96	3
+ 769	6
+ 176	7
+ 45	8
+ 21	7
+ 218	0
+ 253	8
+ 113	3
+ 870	7
+ 715	2
+ 167	6
+ 463	0
+ 947	8
+ 312	6
+ 87	8
+ 312	2
+ 157	1
+ 770	3
+ 787	8
+ 163	8
+ 551	4
+ 818	8
+ 150	9
+ 74	0
+ 583	8
+ 182	8
+ 414	6
+ 755	4
+ 397	1
+ 974	5
+ 886	3
+ 668	0
+ 368	4
+ 377	2
+ 253	5
+ 964	8
+ 921	8
+ 609	1
+ 713	7
+ 90	3
+ 472	3
+ 47	9
+ 917	8
+ 247	3
+ 869	2
+ 798	8
+ 508	5
+ 798	9
+ 905	2
+ 32	2
+ 714	10
+ 962	6
+ 777	6
+ 705	5
+ 253	8
+ 787	7
+ 67	8
+ 612	10
+ 636	9
+ 298	5
+ 80	1
+ 259	6
+ 563	1
+ 464	5
+ 232	5
+ 625	9
+ 491	6
+ 581	3
+ 157	3
+ 759	4
+ 82	5
+ 136	1
+ 380	7
+ 132	0
+ 607	4
+ 520	7
+ 526	8
+ 275	1
+ 837	7
+ 556	1
+ 235	2
+ 15	7
+ 768	6
+ 994	9
+ 882	8
+ 336	10
+ 299	5
+ 112	7
+ 220	2
+ 695	8
+ 675	2
+ 513	2
+ 995	8
+ 290	8
+ 527	8
+ 900	8
+ 27	9
+ 488	8
+ 510	5
+ 720	4
+ 235	1
+ 355	5
+ 528	5
+ 213	7
+ 711	9
+ 574	4
+ 122	1
+ 587	1
+ 876	9
+ 948	4
+ 723	8
+ 165	7
+ 764	7
+ 545	3
+ 134	3
+ 666	4
+ 321	0
+ 903	8
+ 489	1
+ 597	2
+ 23	2
+ 586	1
+ 259	2
+ 263	1
+ 50	2
+ 537	8
+ 60	7
+ 522	8
+ 354	1
+ 98	5
+ 331	8
+ 857	7
+ 786	8
+ 501	3
+ 876	1
+ 475	9
+ 269	1
+ 45	5
+ 234	3
+ 662	3
+ 518	2
+ 56	6
+ 900	6
+ 402	3
+ 644	5
+ 743	10
+ 264	6
+ 627	1
+ 360	1
+ 325	2
+ 226	8
+ 134	5
+ 861	2
+ 22	1
+ 486	7
+ 379	0
+ 882	4
+ 582	8
+ 12	10
+ 37	7
+ 484	8
+ 631	7
+ 379	3
+ 799	7
+ 387	1
+ 974	6
+ 925	1
+ 108	8
+ 287	1
+ 881	8
+ 813	3
+ 778	7
+ 695	4
+ 478	7
+ 344	5
+ 364	8
+ 294	10
+ 577	7
+ 254	4
+ 412	6
+ 500	4
+ 254	4
+ 495	4
+ 211	8
+ 491	1
+ 555	3
+ 352	3
+ 1000	0
+ 693	5
+ 755	0
+ 992	1
+ 865	3
+ 114	4
+ 959	4
+ 818	4
+ 9	3
+ 757	3
+ 743	3
+ 625	10
+ 34	1
+ 46	6
+ 421	4
+ 923	4
+ 445	6
+ 898	2
+ 653	9
+ 319	5
+ 175	4
+ 960	1
+ 802	8
+ 505	8
+ 96	3
+ 75	8
+ 515	7
+ 793	5
+ 817	8
+ 139	2
+ 236	1
+ 658	7
+ 677	6
+ 882	3
+ 445	2
+ 848	6
+ 635	8
+ 754	4
+ 586	3
+ 249	7
+ 523	3
+ 522	0
+ 24	3
+ 587	8
+ 153	7
+ 78	4
+ 787	7
+ 71	5
+ 291	10
+ 794	7
+ 155	6
+ 356	8
+ 451	1
+ 228	0
+ 370	5
+ 719	9
+ 801	2
+ 930	8
+ 556	5
+ 667	7
+ 242	7
+ 98	0
+ 481	2
+ 493	8
+ 123	3
+ 508	3
+ 929	9
+ 68	4
+ 974	3
+ 417	3
+ 772	1
+ 237	6
+ 378	2
+ 399	9
+ 683	1
+ 642	9
+ 811	7
+ 954	3
+ 910	4
+ 64	0
+ 734	6
+ 310	7
+ 437	4
+ 43	4
+ 674	5
+ 756	4
+ 596	10
+ 20	10
+ 158	4
+ 907	8
+ 485	5
+ 766	3
+ 290	7
+ 588	2
+ 167	7
+ 233	9
+ 224	5
+ 564	7
+ 922	6
+ 73	6
+ 67	8
+ 41	7
+ 820	1
+ 637	10
+ 480	5
+ 820	10
+ 94	6
+ 260	4
+ 306	8
+ 584	5
+ 500	8
+ 374	7
+ 361	9
+ 385	3
+ 545	5
+ 877	6
+ 286	9
+ 275	1
+ 979	9
+ 85	5
+ 457	9
+ 424	6
+ 492	7
+ 936	8
+ 531	5
+ 271	0
+ 337	6
+ 755	7
+ 583	1
+ 980	1
+ 599	9
+ 739	9
+ 776	0
+ 992	8
+ 926	1
+ 215	4
+ 982	6
+ 935	5
+ 322	9
+ 272	9
+ 391	5
+ 885	7
+ 189	6
+ 426	8
+ 780	4
+ 899	4
+ 264	6
+ 264	0
+ 652	3
+ 796	6
+ 332	0
+ 961	3
+ 649	9
+ 789	10
+ 767	1
+ 825	2
+ 605	7
+ 886	8
+ 349	3
+ 566	1
+ 719	5
+ 508	10
+ 103	8
+ 23	8
+ 28	8
+ 333	4
+ 830	3
+ 675	5
+ 190	5
+ 450	10
+ 525	3
+ 115	1
+ 984	0
+ 924	3
+ 313	5
+ 463	0
+ 955	10
+ 15	1
+ 743	0
+ 813	8
+ 858	1
+ 131	7
+ 440	8
+ 167	6
+ 270	6
+ 587	8
+ 892	7
+ 925	9
+ 702	8
+ 210	0
+ 339	7
+ 47	3
+ 643	1
+ 351	4
+ 101	2
+ 157	10
+ 310	3
+ 647	7
+ 93	8
+ 380	4
+ 432	10
+ 158	3
+ 668	1
+ 201	4
+ 933	4
+ 386	3
+ 83	4
+ 566	7
+ 496	9
+ 113	6
+ 81	3
+ 556	4
+ 557	2
+ 140	7
+ 16	5
+ 13	4
+ 487	2
+ 772	2
+ 253	10
+ 526	2
+ 384	9
+ 458	5
+ 345	0
+ 194	8
+ 941	3
+ 438	0
+ 577	10
+ 413	1
+ 196	6
+ 784	2
+ 74	8
+ 660	6
+ 967	4
+ 716	2
+ 405	2
+ 407	8
+ 154	9
+ 256	5
+ 888	4
+ 341	8
+ 757	8
+ 852	3
+ 771	3
+ 468	10
+ 819	3
+ 179	9
+ 49	8
+ 454	0
+ 271	2
+ 238	7
+ 413	6
+ 465	6
+ 509	7
+ 67	4
+ 171	4
+ 226	9
+ 186	1
+ 261	10
+ 343	7
+ 924	2
+ 982	1
+ 55	0
+ 942	5
+ 48	2
+ 679	3
+ 890	1
+ 930	4
+ 659	4
+ 75	7
+ 836	2
+ 133	1
+ 173	3
+ 141	4
+ 277	5
+ 164	2
+ 646	1
+ 305	7
+ 178	2
+ 210	2
+ 460	9
+ 512	4
+ 981	4
+ 705	6
+ 881	8
+ 366	7
+ 26	5
+ 780	2
+ 818	9
+ 634	1
+ 404	8
+ 296	0
+ 945	6
+ 751	1
+ 848	10
+ 349	3
+ 850	9
+ 658	8
+ 303	4
+ 471	2
+ 143	8
+ 902	2
+ 335	7
+ 368	2
+ 602	0
+ 248	0
+ 800	5
+ 55	7
+ 145	8
+ 868	10
+ 767	2
+ 301	6
+ 78	10
+ 447	4
+ 322	9
+ 566	5
+ 754	5
+ 633	1
+ 149	0
+ 242	8
+ 2	5
+ 757	8
+ 35	8
+ 547	2
+ 618	4
+ 174	4
+ 631	5
+ 1	7
+ 434	4
+ 91	8
+ 366	7
+ 221	1
+ 124	9
+ 208	3
+ 855	5
+ 25	9
+ 941	8
+ 660	10
+ 593	2
+ 157	2
+ 621	3
+ 596	3
+ 806	6
+ 962	2
+ 45	1
+ 996	4
+ 709	2
+ 530	8
+ 72	7
+ 107	9
+ 189	1
+ 784	1
+ 913	4
+ 106	5
+ 650	3
+ 717	3
+ 594	3
+ 524	4
+ 910	5
+ 640	10
+ 538	6
+ 365	2
+ 854	9
+ 80	9
+ 634	2
+ 852	8
+ 318	6
+ 953	2
+ 80	1
+ 737	7
+ 323	5
+ 2	9
+ 766	5
+ 317	7
+ 11	10
+ 630	5
+ 593	10
+ 795	4
+ 891	9
+ 372	5
+ 61	2
+ 348	4
+ 861	3
+ 610	9
+ 360	3
+ 672	7
+ 800	7
+ 599	6
+ 199	9
+ 242	2
+ 873	9
+ 759	5
+ 868	6
+ 912	8
+ 429	3
+ 284	5
+ 507	6
+ 869	4
+ 933	5
+ 309	3
+ 825	10
+ 976	6
+ 654	6
+ 190	9
+ 491	4
+ 63	4
+ 304	8
+ 829	2
+ 377	7
+ 931	8
+ 24	2
+ 295	5
+ 848	2
+ 899	8
+ 642	2
+ 74	5
+ 188	0
+ 92	8
+ 624	3
+ 695	1
+ 714	8
+ 479	0
+ 581	3
+ 191	10
+ 49	1
+ 763	1
+ 337	1
+ 604	2
+ 222	5
+ 965	9
+ 712	0
+ 332	9
+ 88	4
+ 742	7
+ 706	4
+ 828	4
+ 196	3
+ 438	8
+ 616	6
+ 735	7
+ 751	5
+ 738	1
+ 556	3
+ 272	8
+ 846	2
+ 643	6
+ 277	10
+ 457	4
+ 398	2
+ 77	1
+ 636	9
+ 524	8
+ 213	10
+ 609	8
+ 591	3
+ 493	3
+ 842	2
+ 430	4
+ 573	7
+ 177	4
+ 940	8
+ 977	2
+ 795	4
+ 581	2
+ 633	7
+ 297	3
+ 564	8
+ 101	8
+ 783	7
+ 605	4
+ 55	1
+ 716	9
+ 329	1
+ 296	9
+ 847	5
+ 321	8
+ 294	3
+ 3	1
+ 731	6
+ 281	4
+ 243	6
+ 634	8
+ 399	7
+ 583	2
+ 445	2
+ 555	5
+ 286	3
+ 398	6
+ 417	7
+ 517	3
+ 167	8
+ 51	5
+ 135	1
+ 549	9
+ 638	8
+ 231	9
+ 409	9
+ 687	8
+ 599	3
+ 989	0
+ 458	5
+ 545	7
+ 816	9
+ 359	2
+ 637	9
+ 496	8
+ 713	5
+ 265	8
+ 601	8
+ 715	2
+ 645	9
+ 119	1
+ 810	8
+ 862	4
+ 76	9
+ 454	5
+ 395	10
+ 279	2
+ 942	6
+ 442	6
+ 513	9
+ 383	2
+ 486	6
+ 73	1
+ 463	8
+ 325	1
+ 733	4
+ 162	5
+ 251	0
+ 952	3
+ 874	4
+ 862	3
+ 405	1
+ 479	3
+ 778	9
+ 925	3
+ 860	3
+ 516	3
+ 956	6
+ 433	4
+ 377	8
+ 527	1
+ 203	7
+ 654	5
+ 713	6
+ 781	6
+ 12	6
+ 856	4
+ 783	3
+ 763	6
+ 257	7
+ 852	1
+ 995	4
+ 463	10
+ 957	9
+ 369	3
+ 654	9
+ 445	9
+ 584	1
+ 310	3
+ 704	1
+ 884	7
+ 734	7
+ 132	5
+ 75	9
+ 79	3
+ 582	9
+ 449	6
+ 299	9
+ 527	3
+ 808	9
+ 590	5
+ 791	0
+ 318	4
+ 134	6
+ 671	8
+ 721	6
+ 554	5
+ 295	7
+ 973	4
+ 582	1
+ 702	2
+ 983	2
+ 741	3
+ 63	3
+ 538	9
+ 163	1
+ 333	10
+ 164	7
+ 329	3
+ 280	10
+ 136	0
+ 555	7
+ 455	8
+ 377	4
+ 220	10
+ 480	9
+ 122	5
+ 72	9
+ 744	1
+ 130	3
+ 6	3
+ 410	3
+ 248	6
+ 989	6
+ 872	3
+ 577	0
+ 270	1
+ 697	7
+ 981	1
+ 153	2
+ 32	6
+ 122	2
+ 96	2
+ 16	8
+ 329	1
+ 122	3
+ 440	5
+ 673	7
+ 107	7
+ 265	10
+ 932	8
+ 986	2
+ 972	7
+ 927	10
+ 757	1
+ 154	8
+ 713	3
+ 942	8
+ 470	10
+ 649	8
+ 104	8
+ 134	5
+ 305	8
+ 232	4
+ 469	5
+ 390	4
+ 338	4
+ 602	3
+ 58	5
+ 264	8
+ 609	4
+ 603	3
+ 694	5
+ 131	2
+ 503	8
+ 962	6
+ 552	1
+ 152	9
+ 902	4
+ 268	4
+ 881	7
+ 772	2
+ 33	4
+ 529	1
+ 903	8
+ 905	5
+ 210	5
+ 833	9
+ 53	10
+ 67	6
+ 744	0
+ 164	3
+ 125	3
+ 153	0
+ 699	4
+ 398	6
+ 78	2
+ 798	1
+ 544	3
+ 202	4
+ 119	1
+ 959	3
+ 615	8
+ 232	7
+ 756	3
+ 224	5
+ 328	4
+ 797	5
+ 703	10
+ 480	4
+ 371	9
+ 982	4
+ 49	8
+ 561	6
+ 106	8
+ 40	2
+ 869	10
+ 554	5
+ 790	8
+ 151	5
+ 85	4
+ 47	4
+ 763	8
+ 866	5
+ 794	3
+ 868	2
+ 225	8
+ 615	3
+ 629	2
+ 866	7
+ 937	9
+ 960	8
+ 904	5
+ 290	7
+ 301	4
+ 241	4
+ 816	3
+ 799	6
+ 131	7
+ 45	9
+ 12	9
+ 90	2
+ 762	7
+ 510	4
+ 880	4
+ 126	8
+ 282	1
+ 623	2
+ 601	9
+ 880	9
+ 354	1
+ 287	2
+ 408	1
+ 749	5
+ 753	8
+ 464	8
+ 707	6
+ 2	5
+ 258	5
+ 859	1
+ 888	10
+ 956	2
+ 71	6
+ 355	7
+ 492	2
+ 574	8
+ 355	9
+ 15	8
+ 948	8
+ 302	7
+ 558	8
+ 466	3
+ 320	5
+ 733	6
+ 980	6
+ 716	9
+ 577	7
+ 37	6
+ 251	4
+ 321	7
+ 627	9
+ 588	10
+ 756	6
+ 746	7
+ 367	0
+ 405	9
+ 814	9
+ 191	1
+ 338	9
+ 712	3
+ 517	4
+ 186	1
+ 100	2
+ 743	4
+ 615	1
+ 93	2
+ 958	7
+ 225	7
+ 284	10
+ 418	7
+ 19	8
+ 577	8
+ 693	8
+ 967	0
+ 692	7
+ 349	2
+ 106	5
+ 303	2
+ 758	0
+ 557	4
+ 109	7
+ 616	1
+ 332	8
+ 782	6
+ 812	2
+ 267	8
+ 22	8
+ 665	7
+ 612	6
+ 746	3
+ 309	1
+ 512	4
+ 630	8
+ 622	4
+ 860	2
+ 762	10
+ 830	4
+ 37	2
+ 219	8
+ 777	0
+ 19	0
+ 863	0
+ 888	5
+ 756	5
+ 159	5
+ 804	5
+ 597	3
+ 884	2
+ 131	5
+ 616	10
+ 685	4
+ 961	5
+ 756	10
+ 675	10
+ 818	5
+ 6	8
+ 496	9
+ 878	4
+ 397	6
+ 884	6
+ 135	7
+ 23	7
+ 3	9
+ 959	1
+ 412	6
+ 125	1
+ 953	1
+ 611	7
+ 84	3
+ 683	9
+ 739	7
+ 738	2
+ 559	6
+ 619	10
+ 249	5
+ 511	4
+ 190	5
+ 116	2
+ 442	1
+ 327	9
+ 649	5
+ 951	6
+ 538	6
+ 310	6
+ 848	10
+ 524	6
+ 684	3
+ 822	2
+ 878	4
+ 198	1
+ 943	7
+ 512	1
+ 244	6
+ 325	7
+ 702	7
+ 539	4
+ 104	5
+ 952	6
+ 52	3
+ 264	9
+ 257	8
+ 487	9
+ 50	3
+ 183	9
+ 748	4
+ 56	7
+ 91	6
+ 823	3
+ 195	1
+ 21	9
+ 801	6
+ 247	9
+ 50	2
+ 546	1
+ 462	8
+ 2	7
+ 597	5
+ 659	6
+ 797	8
+ 575	5
+ 224	6
+ 236	3
+ 198	1
+ 650	4
+ 208	7
+ 289	0
+ 231	5
+ 913	3
+ 735	5
+ 383	2
+ 268	4
+ 915	9
+ 874	6
+ 512	7
+ 417	1
+ 215	6
+ 718	5
+ 955	9
+ 511	6
+ 309	7
+ 275	6
+ 727	5
+ 133	6
+ 786	9
+ 99	2
+ 64	4
+ 554	10
+ 233	4
+ 554	7
+ 98	10
+ 832	3
+ 611	5
+ 765	6
+ 466	3
+ 170	8
+ 995	4
+ 371	7
+ 951	5
+ 363	7
+ 371	5
+ 907	4
+ 830	5
+ 414	1
+ 889	10
+ 808	10
+ 937	6
+ 301	5
+ 189	1
+ 114	7
+ 343	3
+ 429	3
+ 729	8
+ 61	7
+ 304	4
+ 416	7
+ 886	3
+ 110	7
+ 784	5
+ 779	7
+ 491	6
+ 660	4
+ 226	10
+ 976	4
+ 28	1
+ 71	4
+ 374	5
+ 709	1
+ 300	8
+ 782	6
+ 193	2
+ 280	1
+ 521	4
+ 794	3
+ 913	6
+ 978	4
+ 159	6
+ 833	4
+ 600	8
+ 801	6
+ 899	9
+ 999	3
+ 371	7
+ 376	7
+ 477	2
+ 276	7
+ 356	6
+ 749	9
+ 945	5
+ 183	9
+ 116	2
+ 262	3
+ 799	1
+ 661	4
+ 904	5
+ 28	8
+ 334	0
+ 76	7
+ 735	5
+ 376	2
+ 609	7
+ 882	10
+ 207	6
+ 843	2
+ 174	0
+ 10	3
+ 187	3
+ 565	10
+ 366	2
+ 386	3
+ 689	4
+ 73	0
+ 441	1
+ 727	2
+ 600	1
+ 388	2
+ 756	3
+ 176	10
+ 901	0
+ 115	1
+ 45	1
+ 364	2
+ 396	9
+ 218	8
+ 156	6
+ 32	8
+ 18	1
+ 867	5
+ 254	6
+ 635	9
+ 699	0
+ 65	5
+ 293	2
+ 417	2
+ 259	5
+ 268	3
+ 656	6
+ 535	1
+ 562	8
+ 814	7
+ 357	8
+ 563	4
+ 952	4
+ 834	2
+ 25	5
+ 60	7
+ 492	1
+ 178	8
+ 365	6
+ 977	6
+ 127	2
+ 928	8
+ 877	5
+ 834	4
+ 216	6
+ 157	6
+ 495	7
+ 949	4
+ 150	8
+ 653	2
+ 252	7
+ 898	7
+ 838	1
+ 527	2
+ 671	5
+ 827	8
+ 750	8
+ 581	6
+ 217	4
+ 66	4
+ 64	2
+ 7	6
+ 943	10
+ 6	1
+ 738	7
+ 267	10
+ 372	2
+ 733	2
+ 242	3
+ 413	9
+ 765	2
+ 712	5
+ 994	3
+ 142	2
+ 708	2
+ 645	8
+ 431	7
+ 331	4
+ 608	3
+ 466	3
+ 996	7
+ 336	4
+ 899	1
+ 577	1
+ 330	10
+ 54	1
+ 229	8
+ 609	2
+ 59	8
+ 435	8
+ 959	1
+ 539	4
+ 733	9
+ 763	3
+ 207	2
+ 687	2
+ 961	0
+ 570	9
+ 93	1
+ 1	4
+ 137	1
+ 517	4
+ 821	1
+ 590	9
+ 878	0
+ 646	8
+ 106	2
+ 226	8
+ 56	10
+ 180	3
+ 218	9
+ 466	2
+ 891	0
+ 39	10
+ 183	0
+ 407	3
+ 95	9
+ 686	9
+ 51	3
+ 795	9
+ 301	4
+ 765	4
+ 627	10
+ 246	7
+ 981	4
+ 946	2
+ 294	4
+ 378	2
+ 448	4
+ 170	6
+ 457	6
+ 950	6
+ 501	6
+ 467	6
+ 911	3
+ 480	2
+ 704	2
+ 619	3
+ 237	9
+ 14	2
+ 291	10
+ 416	6
+ 372	8
+ 770	8
+ 212	9
+ 451	7
+ 516	4
+ 221	0
+ 37	7
+ 568	9
+ 950	0
+ 160	7
+ 293	8
+ 985	5
+ 644	10
+ 747	9
+ 960	2
+ 519	3
+ 958	3
+ 151	2
+ 227	6
+ 838	7
+ 3	1
+ 760	0
+ 747	3
+ 988	7
+ 376	1
+ 351	7
+ 928	3
+ 198	6
+ 336	9
+ 506	3
+ 109	0
+ 627	1
+ 312	8
+ 236	5
+ 380	1
+ 283	4
+ 133	0
+ 423	9
+ 371	4
+ 577	7
+ 559	9
+ 415	5
+ 264	6
+ 58	6
+ 559	6
+ 896	7
+ 588	5
+ 734	9
+ 302	10
+ 440	7
+ 45	7
+ 66	2
+ 766	5
+ 58	1
+ 899	6
+ 883	5
+ 562	3
+ 945	8
+ 911	0
+ 427	5
+ 566	3
+ 138	2
+ 847	9
+ 55	1
+ 842	5
+ 832	9
+ 218	9
+ 66	10
+ 386	1
+ 120	3
+ 758	0
+ 743	3
+ 300	7
+ 147	2
+ 690	6
+ 681	3
+ 898	8
+ 411	7
+ 691	5
+ 895	5
+ 960	7
+ 420	2
+ 625	5
+ 162	0
+ 610	3
+ 296	4
+ 283	0
+ 688	6
+ 726	8
+ 794	4
+ 410	5
+ 673	3
+ 294	1
+ 54	10
+ 549	9
+ 518	5
+ 677	9
+ 687	3
+ 425	8
+ 314	0
+ 130	6
+ 402	4
+ 648	1
+ 997	4
+ 926	8
+ 791	3
+ 267	5
+ 645	6
+ 547	7
+ 546	1
+ 649	1
+ 605	3
+ 3	3
+ 628	4
+ 141	9
+ 462	3
+ 551	9
+ 684	2
+ 954	7
+ 574	9
+ 472	4
+ 217	7
+ 828	9
+ 299	4
+ 562	8
+ 471	2
+ 909	1
+ 536	9
+ 367	2
+ 339	5
+ 106	8
+ 779	7
+ 664	5
+ 856	6
+ 144	4
+ 499	6
+ 796	7
+ 353	6
+ 579	7
+ 999	1
+ 497	5
+ 351	4
+ 546	9
+ 317	9
+ 51	7
+ 421	2
+ 456	2
+ 813	1
+ 664	7
+ 738	8
+ 100	2
+ 422	9
+ 953	8
+ 520	5
+ 428	5
+ 672	9
+ 990	0
+ 331	5
+ 910	6
+ 448	10
+ 305	9
+ 118	8
+ 70	9
+ 881	7
+ 601	6
+ 541	7
+ 855	10
+ 597	8
+ 739	1
+ 341	2
+ 637	0
+ 93	6
+ 37	4
+ 162	9
+ 73	6
+ 908	4
+ 480	0
+ 139	6
+ 957	0
+ 284	6
+ 638	8
+ 259	5
+ 788	9
+ 302	5
+ 974	6
+ 695	6
+ 656	8
+ 237	7
+ 212	4
+ 639	3
+ 9	5
+ 663	5
+ 573	8
+ 39	5
+ 821	3
+ 88	5
+ 148	3
+ 952	9
+ 204	3
+ 464	2
+ 896	2
+ 789	6
+ 947	0
+ 244	2
+ 425	9
+ 444	4
+ 430	1
+ 924	0
+ 909	10
+ 533	7
+ 286	6
+ 189	4
+ 969	1
+ 370	2
+ 394	8
+ 350	3
+ 993	1
+ 842	9
+ 165	1
+ 99	6
+ 969	5
+ 24	4
+ 651	9
+ 401	6
+ 911	9
+ 290	2
+ 556	5
+ 631	5
+ 619	0
+ 696	0
+ 835	0
+ 303	8
+ 185	1
+ 767	3
+ 231	9
+ 940	2
+ 410	10
+ 598	1
+ 912	10
+ 621	8
+ 934	9
+ 20	5
+ 389	7
+ 14	0
+ 651	7
+ 22	5
+ 757	3
+ 313	9
+ 471	1
+ 292	7
+ 947	2
+ 902	4
+ 196	5
+ 418	1
+ 500	0
+ 931	4
+ 949	10
+ 924	3
+ 601	9
+ 348	3
+ 648	4
+ 738	4
+ 695	1
+ 347	2
+ 132	6
+ 867	1
+ 872	8
+ 436	1
+ 269	9
+ 176	8
+ 893	1
+ 203	8
+ 59	1
+ 181	7
+ 65	5
+ 912	7
+ 898	7
+ 118	6
+ 702	5
+ 758	8
+ 105	6
+ 913	10
+ 395	3
+ 45	7
+ 204	2
+ 433	1
+ 329	6
+ 939	4
+ 764	1
+ 48	8
+ 650	10
+ 542	5
+ 610	7
+ 141	3
+ 126	9
+ 146	2
+ 525	1
+ 208	9
+ 409	3
+ 584	6
+ 474	0
+ 710	8
+ 654	6
+ 190	4
+ 770	2
+ 247	4
+ 198	8
+ 968	8
+ 448	1
+ 121	6
+ 8	3
+ 805	5
+ 326	0
+ 452	7
+ 265	0
+ 347	7
+ 53	1
+ 542	7
+ 706	7
+ 124	5
+ 970	4
+ 896	2
+ 159	9
+ 977	6
+ 972	1
+ 182	10
+ 364	10
+ 513	7
+ 999	10
+ 425	3
+ 1000	8
+ 3	1
+ 829	5
+ 759	5
+ 277	9
+ 12	2
+ 254	9
+ 415	4
+ 772	4
+ 21	7
+ 490	2
+ 725	9
+ 189	2
+ 544	2
+ 202	10
+ 452	2
+ 741	5
+ 254	6
+ 1000	0
+ 106	3
+ 896	1
+ 523	1
+ 27	9
+ 563	8
+ 330	6
+ 544	8
+ 786	3
+ 674	10
+ 506	2
+ 162	7
+ 186	6
+ 910	9
+ 69	2
+ 496	1
+ 177	6
+ 346	1
+ 720	9
+ 223	7
+ 807	8
+ 546	1
+ 369	1
+ 958	2
+ 358	6
+ 129	9
+ 849	3
+ 573	0
+ 906	5
+ 961	10
+ 646	5
+ 45	8
+ 59	4
+ 896	8
+ 259	1
+ 526	1
+ 904	1
+ 204	3
+ 162	2
+ 428	5
+ 793	6
+ 385	6
+ 849	10
+ 676	8
+ 440	6
+ 731	1
+ 94	8
+ 909	2
+ 166	8
+ 933	4
+ 923	5
+ 492	8
+ 531	7
+ 100	7
+ 858	5
+ 214	7
+ 86	6
+ 292	9
+ 556	10
+ 691	10
+ 604	4
+ 82	7
+ 197	10
+ 851	4
+ 796	8
+ 788	7
+ 243	3
+ 547	8
+ 975	6
+ 467	8
+ 176	7
+ 484	3
+ 279	8
+ 198	8
+ 743	9
+ 832	3
+ 310	9
+ 46	5
+ 906	9
+ 871	7
+ 681	7
+ 422	9
+ 938	10
+ 698	9
+ 615	2
+ 747	8
+ 846	2
+ 53	1
+ 6	3
+ 961	7
+ 139	8
+ 97	4
+ 707	1
+ 957	6
+ 40	8
+ 314	7
+ 487	7
+ 645	4
+ 704	3
+ 339	3
+ 508	1
+ 110	4
+ 315	2
+ 479	3
+ 414	4
+ 70	6
+ 231	2
+ 3	9
+ 311	10
+ 550	4
+ 788	9
+ 72	3
+ 600	7
+ 700	3
+ 60	0
+ 623	6
+ 124	7
+ 922	4
+ 897	4
+ 760	3
+ 839	8
+ 864	1
+ 998	9
+ 9	3
+ 827	6
+ 660	6
+ 423	7
+ 891	0
+ 450	6
+ 327	5
+ 630	10
+ 78	8
+ 685	0
+ 194	6
+ 401	10
+ 893	2
+ 785	8
+ 311	8
+ 625	3
+ 92	5
+ 878	8
+ 68	3
+ 484	10
+ 325	9
+ 550	7
+ 444	2
+ 603	5
+ 935	3
+ 522	1
+ 870	9
+ 82	8
+ 163	9
+ 521	5
+ 650	1
+ 794	7
+ 598	7
+ 494	7
+ 974	10
+ 625	3
+ 911	2
+ 951	4
+ 356	6
+ 877	3
+ 842	4
+ 419	7
+ 322	5
+ 476	5
+ 369	10
+ 960	0
+ 143	8
+ 761	7
+ 426	3
+ 408	4
+ 233	0
+ 698	1
+ 209	6
+ 499	6
+ 203	4
+ 856	0
+ 775	3
+ 757	1
+ 776	2
+ 583	1
+ 229	5
+ 164	4
+ 297	9
+ 114	7
+ 180	5
+ 122	4
+ 555	8
+ 556	8
+ 469	1
+ 328	7
+ 431	2
+ 717	2
+ 459	5
+ 302	2
+ 706	9
+ 380	9
+ 428	5
+ 308	7
+ 468	4
+ 447	6
+ 944	6
+ 60	5
+ 390	6
+ 262	9
+ 672	6
+ 531	1
+ 774	2
+ 307	2
+ 721	6
+ 468	4
+ 495	8
+ 363	9
+ 392	7
+ 648	9
+ 93	1
+ 508	0
+ 664	6
+ 536	1
+ 185	8
+ 913	9
+ 390	4
+ 959	2
+ 692	3
+ 397	4
+ 877	9
+ 841	4
+ 713	2
+ 295	1
+ 875	9
+ 965	10
+ 37	5
+ 6	7
+ 42	5
+ 755	2
+ 342	7
+ 84	7
+ 112	0
+ 895	8
+ 310	3
+ 218	2
+ 158	1
+ 559	9
+ 264	9
+ 976	1
+ 796	9
+ 108	8
+ 413	1
+ 533	5
+ 658	3
+ 682	10
+ 956	8
+ 731	1
+ 809	6
+ 873	1
+ 918	1
+ 307	1
+ 152	9
+ 947	4
+ 719	9
+ 555	5
+ 863	7
+ 347	3
+ 778	9
+ 731	4
+ 168	4
+ 435	1
+ 179	2
+ 193	10
+ 791	1
+ 108	7
+ 159	4
+ 786	3
+ 280	7
+ 726	10
+ 655	3
+ 512	5
+ 944	9
+ 793	7
+ 739	5
+ 158	9
+ 937	6
+ 32	1
+ 758	2
+ 104	5
+ 292	2
+ 259	5
+ 626	0
+ 761	9
+ 777	5
+ 903	4
+ 767	4
+ 949	7
+ 274	7
+ 434	0
+ 266	6
+ 921	2
+ 184	10
+ 318	9
+ 178	4
+ 491	5
+ 633	8
+ 921	3
+ 795	7
+ 164	6
+ 168	1
+ 3	9
+ 483	10
+ 647	8
+ 694	1
+ 771	10
+ 673	7
+ 163	9
+ 644	5
+ 799	8
+ 903	3
+ 292	5
+ 40	2
+ 794	8
+ 895	10
+ 407	1
+ 25	4
+ 999	5
+ 362	6
+ 265	1
+ 727	0
+ 16	4
+ 727	2
+ 257	4
+ 660	1
+ 193	6
+ 345	5
+ 98	4
+ 698	9
+ 221	6
+ 850	6
+ 656	9
+ 37	7
+ 383	4
+ 301	6
+ 455	0
+ 684	5
+ 428	4
+ 650	7
+ 781	3
+ 740	10
+ 872	1
+ 459	10
+ 471	2
+ 863	7
+ 749	7
+ 319	4
+ 589	4
+ 59	10
+ 755	4
+ 621	2
+ 388	3
+ 681	8
+ 716	3
+ 501	5
+ 641	2
+ 471	5
+ 327	9
+ 484	8
+ 87	3
+ 490	8
+ 60	8
+ 241	6
+ 166	3
+ 622	9
+ 661	2
+ 132	0
+ 547	8
+ 865	3
+ 144	4
+ 760	8
+ 606	2
+ 299	9
+ 162	8
+ 731	2
+ 130	2
+ 85	2
+ 30	3
+ 840	2
+ 627	5
+ 117	3
+ 705	2
+ 337	3
+ 61	2
+ 515	2
+ 566	3
+ 991	2
+ 506	3
+ 105	7
+ 74	8
+ 916	2
+ 57	0
+ 395	1
+ 328	2
+ 282	10
+ 697	4
+ 243	4
+ 647	6
+ 654	7
+ 781	2
+ 914	3
+ 444	9
+ 520	9
+ 196	6
+ 618	3
+ 461	5
+ 474	5
+ 535	9
+ 604	9
+ 105	9
+ 818	8
+ 285	1
+ 205	9
+ 641	9
+ 641	4
+ 28	6
+ 768	5
+ 461	3
+ 421	7
+ 913	0
+ 927	4
+ 573	4
+ 892	1
+ 271	5
+ 971	4
+ 382	8
+ 179	7
+ 851	4
+ 600	5
+ 243	2
+ 913	3
+ 796	7
+ 742	3
+ 969	2
+ 914	9
+ 202	8
+ 257	8
+ 243	1
+ 882	5
+ 644	9
+ 891	0
+ 643	1
+ 694	5
+ 454	3
+ 986	7
+ 535	9
+ 967	3
+ 580	7
+ 588	5
+ 871	5
+ 432	1
+ 344	7
+ 847	6
+ 837	7
+ 100	5
+ 583	10
+ 508	2
+ 61	2
+ 721	5
+ 497	7
+ 211	0
+ 606	2
+ 363	2
+ 887	10
+ 736	8
+ 454	2
+ 831	8
+ 858	7
+ 384	7
+ 408	5
+ 176	10
+ 475	7
+ 218	5
+ 887	9
+ 51	4
+ 646	3
+ 415	3
+ 440	8
+ 438	3
+ 729	2
+ 86	2
+ 344	9
+ 981	2
+ 596	4
+ 896	0
+ 850	1
+ 995	3
+ 756	2
+ 861	6
+ 152	9
+ 26	8
+ 174	4
+ 50	6
+ 218	5
+ 942	9
+ 663	0
+ 131	0
+ 944	1
+ 208	5
+ 477	1
+ 544	3
+ 176	5
+ 652	9
+ 752	5
+ 574	9
+ 424	6
+ 702	6
+ 41	8
+ 212	3
+ 241	2
+ 207	9
+ 181	3
+ 911	1
+ 450	1
+ 665	9
+ 222	2
+ 254	4
+ 748	9
+ 329	5
+ 418	9
+ 405	8
+ 504	1
+ 441	5
+ 860	7
+ 803	1
+ 807	0
+ 4	10
+ 348	9
+ 114	8
+ 33	8
+ 725	3
+ 988	10
+ 653	7
+ 885	10
+ 238	3
+ 886	6
+ 146	4
+ 751	6
+ 934	6
+ 240	7
+ 712	0
+ 748	7
+ 35	1
+ 631	1
+ 894	7
+ 928	6
+ 920	9
+ 598	6
+ 654	5
+ 556	9
+ 786	4
+ 535	9
+ 832	3
+ 518	8
+ 896	8
+ 504	6
+ 804	3
+ 324	8
+ 347	10
+ 989	2
+ 619	9
+ 860	5
+ 834	5
+ 113	5
+ 942	7
+ 380	7
+ 112	9
+ 659	9
+ 200	2
+ 711	1
+ 934	2
+ 704	7
+ 466	0
+ 578	8
+ 983	6
+ 55	6
+ 485	9
+ 142	3
+ 373	3
+ 808	3
+ 925	2
+ 43	0
+ 102	7
+ 981	3
+ 878	7
+ 398	8
+ 906	1
+ 551	4
+ 129	1
+ 186	1
+ 697	2
+ 715	2
+ 156	9
+ 502	5
+ 112	3
+ 844	0
+ 497	9
+ 74	6
+ 589	1
+ 900	5
+ 747	3
+ 280	7
+ 399	8
+ 26	5
+ 961	2
+ 641	7
+ 453	4
+ 840	6
+ 212	3
+ 138	3
+ 651	10
+ 362	1
+ 869	4
+ 746	5
+ 490	6
+ 925	2
+ 943	2
+ 890	3
+ 36	9
+ 870	10
+ 128	5
+ 655	6
+ 866	5
+ 190	1
+ 837	3
+ 403	5
+ 310	8
+ 636	2
+ 200	4
+ 637	7
+ 28	6
+ 927	10
+ 766	8
+ 313	8
+ 733	2
+ 798	9
+ 695	5
+ 443	6
+ 948	6
+ 640	8
+ 960	0
+ 274	3
+ 808	9
+ 449	0
+ 292	1
+ 698	3
+ 648	6
+ 291	4
+ 443	6
+ 215	2
+ 788	0
+ 37	5
+ 467	5
+ 44	4
+ 112	7
+ 200	1
+ 727	5
+ 342	5
+ 383	8
+ 542	7
+ 877	2
+ 995	5
+ 866	3
+ 938	3
+ 891	2
+ 484	7
+ 167	5
+ 162	6
+ 1	2
+ 48	1
+ 890	2
+ 186	6
+ 721	5
+ 151	1
+ 318	7
+ 779	2
+ 934	8
+ 719	8
+ 61	7
+ 108	10
+ 810	6
+ 632	10
+ 114	8
+ 610	1
+ 0	7
+ 229	9
+ 906	4
+ 506	6
+ 942	7
+ 731	3
+ 350	5
+ 455	3
+ 284	2
+ 83	3
+ 830	2
+ 297	6
+ 783	9
+ 617	9
+ 723	2
+ 12	7
+ 885	2
+ 614	8
+ 656	1
+ 418	6
+ 777	1
+ 858	1
+ 659	3
+ 411	9
+ 486	5
+ 288	3
+ 685	6
+ 957	5
+ 514	6
+ 365	2
+ 801	4
+ 961	7
+ 618	6
+ 477	3
+ 695	9
+ 871	5
+ 44	7
+ 600	7
+ 42	0
+ 646	5
+ 504	9
+ 845	2
+ 520	8
+ 657	0
+ 375	0
+ 272	2
+ 398	2
+ 862	0
+ 809	3
+ 290	5
+ 234	2
+ 976	3
+ 891	6
+ 982	9
+ 587	6
+ 461	1
+ 563	3
+ 280	1
+ 107	9
+ 117	5
+ 958	4
+ 658	4
+ 623	5
+ 372	4
+ 859	7
+ 935	1
+ 823	9
+ 372	7
+ 488	4
+ 646	1
+ 982	1
+ 165	5
+ 412	4
+ 628	5
+ 382	7
+ 2	3
+ 135	7
+ 696	8
+ 179	1
+ 190	0
+ 730	1
+ 131	6
+ 36	5
+ 266	5
+ 857	9
+ 598	8
+ 19	8
+ 384	4
+ 209	0
+ 951	6
+ 759	10
+ 932	9
+ 612	6
+ 652	8
+ 696	8
+ 830	4
+ 966	10
+ 978	0
+ 464	2
+ 527	3
+ 155	1
+ 160	2
+ 889	5
+ 605	1
+ 556	6
+ 690	3
+ 508	6
+ 209	1
+ 249	9
+ 912	9
+ 703	7
+ 370	7
+ 703	3
+ 672	2
+ 591	2
+ 488	7
+ 324	6
+ 921	2
+ 191	5
+ 311	7
+ 82	0
+ 62	6
+ 621	3
+ 710	9
+ 132	6
+ 815	8
+ 364	2
+ 504	1
+ 533	2
+ 234	1
+ 374	7
+ 872	7
+ 369	8
+ 911	6
+ 319	2
+ 307	4
+ 221	4
+ 990	8
+ 641	7
+ 713	8
+ 323	5
+ 608	7
+ 714	1
+ 755	2
+ 288	10
+ 372	7
+ 711	2
+ 360	1
+ 36	3
+ 640	4
+ 492	9
+ 755	7
+ 317	7
+ 556	10
+ 446	3
+ 731	8
+ 798	3
+ 457	5
+ 450	2
+ 760	7
+ 201	1
+ 400	9
+ 375	8
+ 991	4
+ 31	6
+ 765	5
+ 578	5
+ 237	9
+ 265	8
+ 852	7
+ 63	6
+ 481	9
+ 921	9
+ 374	4
+ 149	1
+ 109	3
+ 265	5
+ 261	6
+ 270	3
+ 50	3
+ 883	8
+ 824	5
+ 335	1
+ 355	6
+ 854	2
+ 312	9
+ 789	8
+ 778	7
+ 730	2
+ 81	9
+ 285	2
+ 228	6
+ 700	5
+ 190	10
+ 740	2
+ 271	6
+ 55	1
+ 84	4
+ 156	4
+ 990	0
+ 646	3
+ 927	4
+ 94	7
+ 145	8
+ 856	2
+ 702	1
+ 417	9
+ 692	1
+ 418	9
+ 87	2
+ 122	4
+ 782	2
+ 453	9
+ 567	6
+ 305	6
+ 619	10
+ 859	5
+ 386	10
+ 250	5
+ 776	1
+ 757	5
+ 249	2
+ 407	9
+ 291	8
+ 823	4
+ 983	9
+ 736	8
+ 121	2
+ 631	7
+ 798	9
+ 245	4
+ 886	1
+ 963	3
+ 57	2
+ 803	8
+ 320	6
+ 310	6
+ 734	7
+ 509	0
+ 543	3
+ 403	5
+ 276	1
+ 291	4
+ 328	9
+ 85	1
+ 858	3
+ 544	7
+ 434	5
+ 16	5
+ 719	8
+ 324	0
+ 378	6
+ 607	1
+ 352	1
+ 137	9
+ 447	5
+ 420	7
+ 679	7
+ 119	0
+ 634	2
+ 134	5
+ 535	7
+ 236	10
+ 184	3
+ 461	9
+ 71	8
+ 942	4
+ 418	5
+ 561	8
+ 664	7
+ 664	1
+ 238	1
+ 834	9
+ 796	10
+ 924	4
+ 158	1
+ 921	7
+ 735	2
+ 662	9
+ 409	1
+ 822	5
+ 907	8
+ 929	3
+ 312	5
+ 95	10
+ 188	8
+ 87	4
+ 844	9
+ 342	6
+ 874	3
+ 69	0
+ 324	10
+ 724	1
+ 148	4
+ 977	6
+ 510	8
+ 38	4
+ 563	10
+ 743	9
+ 458	8
+ 851	6
+ 598	9
+ 72	4
+ 859	4
+ 81	7
+ 680	2
+ 764	0
+ 141	5
+ 63	3
+ 875	0
+ 846	4
+ 839	9
+ 801	4
+ 851	5
+ 277	3
+ 382	1
+ 955	10
+ 65	0
+ 421	9
+ 441	5
+ 656	1
+ 653	4
+ 125	8
+ 908	2
+ 83	8
+ 228	9
+ 167	1
+ 813	10
+ 469	7
+ 513	7
+ 974	9
+ 873	9
+ 875	9
+ 955	3
+ 862	4
+ 799	5
+ 517	5
+ 939	6
+ 245	8
+ 830	3
+ 630	1
+ 257	8
+ 126	1
+ 765	6
+ 734	3
+ 341	7
+ 173	2
+ 637	0
+ 152	6
+ 344	0
+ 988	1
+ 533	5
+ 594	5
+ 148	8
+ 319	10
+ 166	9
+ 37	4
+ 745	2
+ 493	5
+ 757	2
+ 788	1
+ 935	10
+ 311	6
+ 9	5
+ 164	4
+ 478	2
+ 495	0
+ 658	1
+ 483	8
+ 927	8
+ 785	1
+ 751	8
+ 516	5
+ 984	0
+ 7	7
+ 235	8
+ 840	2
+ 756	2
+ 742	8
+ 615	9
+ 118	1
+ 58	6
+ 104	7
+ 700	6
+ 522	6
+ 389	3
+ 720	1
+ 128	2
+ 637	1
+ 244	6
+ 854	5
+ 439	7
+ 650	2
+ 845	4
+ 961	5
+ 298	1
+ 552	4
+ 690	7
+ 72	4
+ 243	6
+ 18	6
+ 901	7
+ 772	0
+ 973	4
+ 142	2
+ 52	10
+ 695	5
+ 691	3
+ 687	5
+ 737	6
+ 995	0
+ 725	5
+ 392	4
+ 203	5
+ 806	4
+ 59	8
+ 77	10
+ 562	8
+ 989	5
+ 258	1
+ 751	3
+ 127	4
+ 802	8
+ 792	5
+ 353	5
+ 136	3
+ 564	9
+ 895	10
+ 278	1
+ 420	1
+ 544	5
+ 908	6
+ 438	5
+ 471	4
+ 5	7
+ 558	8
+ 40	7
+ 203	8
+ 503	10
+ 331	9
+ 523	5
+ 205	1
+ 330	1
+ 42	6
+ 199	5
+ 692	7
+ 941	6
+ 363	4
+ 70	8
+ 806	1
+ 563	4
+ 831	6
+ 49	0
+ 445	6
+ 28	8
+ 408	6
+ 245	6
+ 638	6
+ 713	7
+ 182	9
+ 142	9
+ 654	1
+ 473	0
+ 463	5
+ 852	3
+ 619	4
+ 632	4
+ 18	7
+ 484	5
+ 233	5
+ 240	6
+ 63	5
+ 254	7
+ 59	10
+ 380	2
+ 880	5
+ 114	5
+ 606	6
+ 551	1
+ 131	4
+ 337	7
+ 818	10
+ 199	8
+ 650	7
+ 299	9
+ 195	5
+ 524	3
+ 23	8
+ 958	1
+ 746	3
+ 322	6
+ 860	4
+ 159	5
+ 23	7
+ 535	2
+ 114	9
+ 904	9
+ 842	1
+ 767	5
+ 786	1
+ 375	10
+ 604	9
+ 239	6
+ 678	2
+ 708	4
+ 534	0
+ 49	4
+ 466	2
+ 861	5
+ 919	4
+ 643	0
+ 269	5
+ 964	1
+ 650	7
+ 603	4
+ 797	10
+ 418	4
+ 877	7
+ 28	6
+ 853	7
+ 979	4
+ 766	0
+ 782	2
+ 236	6
+ 721	2
+ 40	4
+ 188	3
+ 911	2
+ 419	6
+ 884	0
+ 999	7
+ 1000	4
+ 82	9
+ 73	1
+ 432	9
+ 846	4
+ 313	6
+ 439	1
+ 844	7
+ 739	6
+ 831	8
+ 929	0
+ 87	8
+ 172	5
+ 402	1
+ 528	4
+ 736	5
+ 817	8
+ 405	9
+ 927	8
+ 817	8
+ 249	1
+ 384	7
+ 225	2
+ 364	10
+ 793	2
+ 742	7
+ 215	8
+ 562	4
+ 336	10
+ 443	9
+ 365	2
+ 392	2
+ 997	8
+ 72	9
+ 635	9
+ 697	9
+ 19	1
+ 573	2
+ 309	9
+ 208	1
+ 132	10
+ 824	3
+ 780	4
+ 734	1
+ 351	2
+ 980	7
+ 356	4
+ 897	4
+ 170	10
+ 276	8
+ 858	10
+ 690	9
+ 54	3
+ 121	4
+ 199	3
+ 466	3
+ 280	3
+ 678	1
+ 677	4
+ 175	0
+ 589	2
+ 743	9
+ 527	6
+ 297	7
+ 610	6
+ 502	5
+ 547	2
+ 345	6
+ 454	5
+ 965	7
+ 795	4
+ 983	1
+ 721	7
+ 135	4
+ 74	3
+ 425	7
+ 465	2
+ 607	10
+ 808	9
+ 689	4
+ 478	2
+ 886	0
+ 382	2
+ 626	8
+ 697	6
+ 488	5
+ 21	5
+ 567	7
+ 133	7
+ 140	2
+ 12	6
+ 869	5
+ 734	5
+ 469	5
+ 381	2
+ 960	9
+ 349	8
+ 884	7
+ 77	5
+ 567	8
+ 100	1
+ 266	1
+ 527	8
+ 864	7
+ 535	0
+ 867	5
+ 570	7
+ 24	3
+ 213	5
+ 845	6
+ 651	8
+ 453	0
+ 651	3
+ 732	7
+ 846	3
+ 501	9
+ 355	8
+ 67	9
+ 600	9
+ 542	1
+ 935	4
+ 682	5
+ 146	7
+ 808	4
+ 199	7
+ 953	9
+ 459	4
+ 851	1
+ 743	6
+ 837	6
+ 882	3
+ 534	2
+ 105	6
+ 118	7
+ 532	7
+ 840	5
+ 70	5
+ 971	2
+ 228	8
+ 575	4
+ 433	5
+ 277	9
+ 935	1
+ 1	7
+ 710	8
+ 266	6
+ 176	8
+ 828	3
+ 402	9
+ 986	9
+ 607	8
+ 399	7
+ 348	4
+ 892	6
+ 150	5
+ 1	6
+ 996	3
+ 474	9
+ 406	5
+ 609	1
+ 312	9
+ 708	5
+ 676	5
+ 768	1
+ 483	8
+ 10	1
+ 580	4
+ 766	9
+ 780	7
+ 502	9
+ 125	5
+ 513	1
+ 782	10
+ 52	2
+ 461	7
+ 304	8
+ 535	0
+ 261	2
+ 548	0
+ 288	0
+ 783	3
+ 121	4
+ 708	9
+ 290	5
+ 545	8
+ 418	7
+ 296	9
+ 791	1
+ 918	8
+ 266	4
+ 504	6
+ 153	0
+ 581	4
+ 250	1
+ 443	5
+ 161	2
+ 836	3
+ 589	5
+ 169	9
+ 32	7
+ 671	4
+ 384	10
+ 381	2
+ 45	3
+ 19	3
+ 678	5
+ 881	8
+ 561	5
+ 244	8
+ 591	7
+ 349	8
+ 913	2
+ 34	5
+ 728	2
+ 380	8
+ 916	1
+ 209	3
+ 18	6
+ 476	1
+ 890	5
+ 374	6
+ 17	3
+ 399	6
+ 716	6
+ 389	3
+ 330	7
+ 60	2
+ 922	1
+ 744	6
+ 296	1
+ 409	2
+ 174	6
+ 513	2
+ 209	10
+ 255	1
+ 483	6
+ 667	5
+ 883	1
+ 78	6
+ 708	5
+ 907	0
+ 204	10
+ 280	1
+ 60	0
+ 775	4
+ 147	2
+ 569	3
+ 803	1
+ 512	0
+ 71	8
+ 111	6
+ 396	8
+ 53	3
+ 842	1
+ 878	6
+ 597	8
+ 588	8
+ 751	9
+ 927	8
+ 891	7
+ 169	0
+ 886	7
+ 359	7
+ 820	9
+ 701	9
+ 638	8
+ 445	0
+ 588	5
+ 312	4
+ 628	2
+ 981	2
+ 975	6
+ 26	7
+ 437	10
+ 538	3
+ 655	7
+ 366	5
+ 445	7
+ 229	3
+ 595	9
+ 156	2
+ 741	6
+ 266	3
+ 98	6
+ 760	7
+ 768	7
+ 952	7
+ 310	10
+ 469	7
+ 931	0
+ 74	6
+ 713	4
+ 127	2
+ 164	4
+ 423	8
+ 286	6
+ 992	0
+ 180	3
+ 357	3
+ 837	1
+ 5	6
+ 858	10
+ 348	2
+ 935	8
+ 915	9
+ 823	10
+ 452	5
+ 429	6
+ 694	6
+ 935	1
+ 352	2
+ 696	3
+ 249	9
+ 602	6
+ 153	4
+ 722	2
+ 44	6
+ 115	4
+ 748	0
+ 208	7
+ 915	0
+ 652	4
+ 566	1
+ 946	3
+ 673	9
+ 377	0
+ 102	1
+ 369	4
+ 948	10
+ 956	1
+ 409	7
+ 259	5
+ 258	4
+ 844	0
+ 423	1
+ 669	3
+ 82	3
+ 705	6
+ 402	7
+ 908	1
+ 533	3
+ 101	6
+ 357	5
+ 986	3
+ 440	9
+ 406	8
+ 620	7
+ 303	9
+ 39	1
+ 885	5
+ 199	6
+ 801	3
+ 875	5
+ 929	3
+ 157	8
+ 353	7
+ 123	5
+ 325	5
+ 923	3
+ 785	4
+ 252	2
+ 213	9
+ 857	5
+ 751	9
+ 663	6
+ 359	9
+ 190	2
+ 142	1
+ 665	1
+ 343	8
+ 909	7
+ 513	0
+ 149	8
+ 513	1
+ 148	3
+ 435	4
+ 489	6
+ 273	3
+ 163	0
+ 243	8
+ 660	6
+ 687	8
+ 761	8
+ 914	4
+ 901	3
+ 249	8
+ 952	8
+ 843	1
+ 600	4
+ 173	7
+ 653	6
+ 149	1
+ 255	4
+ 489	4
+ 446	7
+ 244	1
+ 334	9
+ 955	1
+ 760	9
+ 521	7
+ 126	8
+ 471	1
+ 532	3
+ 180	1
+ 668	4
+ 880	3
+ 961	0
+ 464	2
+ 450	10
+ 634	9
+ 685	9
+ 2	0
+ 809	10
+ 113	6
+ 826	6
+ 230	10
+ 405	7
+ 30	9
+ 14	2
+ 69	7
+ 563	9
+ 3	5
+ 978	5
+ 740	4
+ 420	4
+ 324	1
+ 252	3
+ 123	1
+ 283	2
+ 631	1
+ 871	9
+ 60	3
+ 561	1
+ 213	6
+ 301	3
+ 257	9
+ 232	3
+ 388	2
+ 727	1
+ 637	1
+ 501	10
+ 252	8
+ 288	4
+ 815	6
+ 612	4
+ 678	5
+ 306	7
+ 759	9
+ 829	10
+ 442	1
+ 255	7
+ 994	5
+ 959	4
+ 696	7
+ 509	3
+ 833	0
+ 294	1
+ 764	6
+ 461	6
+ 152	1
+ 25	8
+ 555	3
+ 569	3
+ 199	4
+ 287	6
+ 528	5
+ 339	5
+ 28	3
+ 903	7
+ 983	4
+ 57	8
+ 422	4
+ 902	2
+ 933	4
+ 765	1
+ 435	8
+ 915	10
+ 122	5
+ 304	3
+ 882	6
+ 961	4
+ 133	3
+ 931	2
+ 598	8
+ 885	6
+ 246	9
+ 397	7
+ 292	3
+ 853	2
+ 662	6
+ 310	1
+ 409	2
+ 86	5
+ 709	4
+ 852	6
+ 982	8
+ 1	1
+ 114	9
+ 276	7
+ 766	2
+ 293	0
+ 102	7
+ 680	4
+ 989	5
+ 620	7
+ 152	9
+ 747	6
+ 154	8
+ 92	9
+ 224	9
+ 454	2
+ 758	5
+ 321	9
+ 386	6
+ 584	2
+ 758	9
+ 164	9
+ 567	8
+ 255	6
+ 377	9
+ 207	5
+ 804	10
+ 89	10
+ 788	2
+ 821	0
+ 126	3
+ 218	9
+ 729	5
+ 757	1
+ 136	3
+ 267	9
+ 219	4
+ 755	8
+ 275	0
+ 342	7
+ 885	5
+ 179	7
+ 503	3
+ 648	3
+ 450	5
+ 303	6
+ 743	5
+ 460	5
+ 60	2
+ 587	2
+ 559	9
+ 91	8
+ 285	8
+ 563	6
+ 856	9
+ 211	7
+ 454	4
+ 430	10
+ 659	1
+ 249	1
+ 546	6
+ 685	3
+ 72	1
+ 762	1
+ 363	3
+ 328	9
+ 202	4
+ 699	5
+ 265	3
+ 47	1
+ 168	3
+ 862	6
+ 649	3
+ 580	3
+ 369	8
+ 417	9
+ 379	1
+ 205	5
+ 247	10
+ 583	6
+ 315	9
+ 532	5
+ 331	2
+ 5	6
+ 493	1
+ 717	7
+ 310	6
+ 283	10
+ 870	9
+ 267	2
+ 691	7
+ 154	1
+ 786	4
+ 522	0
+ 326	1
+ 642	6
+ 17	2
+ 158	3
+ 405	2
+ 943	9
+ 215	7
+ 559	5
+ 238	8
+ 484	1
+ 704	8
+ 346	4
+ 435	5
+ 465	2
+ 860	10
+ 253	2
+ 92	9
+ 826	1
+ 70	10
+ 456	5
+ 147	4
+ 373	4
+ 60	9
+ 887	3
+ 774	4
+ 405	5
+ 122	8
+ 873	6
+ 253	3
+ 778	1
+ 326	0
+ 298	4
+ 927	1
+ 527	10
+ 109	10
+ 471	3
+ 383	8
+ 618	4
+ 775	5
+ 740	5
+ 875	1
+ 27	10
+ 897	9
+ 554	1
+ 239	3
+ 263	6
+ 362	6
+ 982	3
+ 686	5
+ 285	8
+ 492	8
+ 51	9
+ 600	7
+ 317	4
+ 173	1
+ 924	0
+ 203	10
+ 45	1
+ 851	6
+ 250	1
+ 930	5
+ 654	3
+ 74	6
+ 581	8
+ 145	9
+ 554	6
+ 623	6
+ 511	2
+ 274	8
+ 598	4
+ 886	5
+ 496	1
+ 474	5
+ 189	3
+ 141	4
+ 414	1
+ 953	1
+ 363	0
+ 704	9
+ 786	8
+ 811	3
+ 485	4
+ 946	10
+ 657	2
+ 825	3
+ 668	7
+ 778	2
+ 800	3
+ 705	10
+ 576	8
+ 429	10
+ 916	4
+ 58	3
+ 409	8
+ 225	2
+ 610	0
+ 536	1
+ 470	5
+ 92	1
+ 702	9
+ 383	4
+ 628	2
+ 533	4
+ 412	2
+ 417	10
+ 84	8
+ 978	0
+ 229	0
+ 280	6
+ 798	5
+ 834	4
+ 540	4
+ 504	0
+ 852	6
+ 138	6
+ 512	5
+ 925	1
+ 682	5
+ 567	1
+ 696	10
+ 82	8
+ 830	1
+ 780	1
+ 96	1
+ 697	9
+ 565	5
+ 302	1
+ 900	8
+ 116	8
+ 401	3
+ 307	9
+ 774	2
+ 52	5
+ 690	6
+ 550	4
+ 603	6
+ 166	4
+ 691	9
+ 493	8
+ 7	2
+ 681	6
+ 720	10
+ 677	6
+ 789	8
+ 374	2
+ 46	7
+ 103	8
+ 913	2
+ 276	6
+ 774	8
+ 989	4
+ 457	2
+ 811	1
+ 102	3
+ 935	1
+ 493	6
+ 680	2
+ 601	4
+ 835	4
+ 149	2
+ 580	2
+ 889	7
+ 14	8
+ 838	3
+ 404	6
+ 115	4
+ 989	6
+ 548	8
+ 720	6
+ 103	7
+ 758	6
+ 272	4
+ 810	9
+ 795	6
+ 262	9
+ 852	8
+ 138	7
+ 525	2
+ 543	4
+ 442	9
+ 975	6
+ 340	10
+ 129	9
+ 764	8
+ 537	9
+ 504	3
+ 463	8
+ 733	3
+ 649	5
+ 916	9
+ 471	8
+ 754	6
+ 510	3
+ 761	1
+ 642	2
+ 1000	6
+ 761	3
+ 581	9
+ 227	3
+ 740	8
+ 211	2
+ 58	7
+ 21	8
+ 946	7
+ 318	9
+ 582	8
+ 631	3
+ 398	1
+ 615	2
+ 194	3
+ 363	2
+ 875	1
+ 533	5
+ 16	8
+ 801	8
+ 522	0
+ 0	6
+ 686	0
+ 371	6
+ 692	7
+ 494	3
+ 478	1
+ 610	9
+ 266	2
+ 36	5
+ 483	4
+ 653	4
+ 524	2
+ 814	5
+ 945	6
+ 296	5
+ 627	3
+ 47	3
+ 318	4
+ 944	0
+ 108	4
+ 283	6
+ 564	9
+ 463	8
+ 118	5
+ 290	6
+ 898	9
+ 959	4
+ 129	8
+ 963	1
+ 388	3
+ 541	0
+ 555	6
+ 327	9
+ 6	3
+ 882	1
+ 711	2
+ 700	3
+ 58	2
+ 105	2
+ 662	4
+ 777	6
+ 338	7
+ 983	5
+ 509	9
+ 540	9
+ 205	1
+ 912	8
+ 669	2
+ 633	7
+ 511	5
+ 790	2
+ 680	5
+ 496	7
+ 653	6
+ 915	3
+ 995	7
+ 875	3
+ 429	9
+ 800	9
+ 804	3
+ 835	0
+ 422	7
+ 768	1
+ 987	4
+ 767	5
+ 915	6
+ 720	6
+ 47	2
+ 334	7
+ 817	2
+ 15	8
+ 941	9
+ 145	4
+ 747	9
+ 307	6
+ 286	1
+ 559	7
+ 890	3
+ 798	9
+ 727	6
+ 375	6
+ 122	1
+ 238	2
+ 311	6
+ 869	1
+ 820	9
+ 941	8
+ 773	1
+ 130	5
+ 31	4
+ 70	3
+ 580	6
+ 24	5
+ 956	8
+ 347	7
+ 387	7
+ 325	5
+ 817	6
+ 678	1
+ 134	5
+ 257	10
+ 431	2
+ 715	2
+ 284	8
+ 724	3
+ 281	8
+ 632	9
+ 423	7
+ 331	4
+ 477	7
+ 62	9
+ 400	4
+ 374	2
+ 950	1
+ 346	1
+ 599	6
+ 38	0
+ 800	8
+ 234	1
+ 597	10
+ 399	9
+ 751	0
+ 740	2
+ 686	1
+ 554	2
+ 749	6
+ 28	1
+ 2	4
+ 366	10
+ 454	7
+ 36	1
+ 314	1
+ 83	1
+ 827	3
+ 198	4
+ 275	6
+ 304	0
+ 628	0
+ 201	3
+ 114	8
+ 477	9
+ 370	5
+ 12	4
+ 907	4
+ 324	4
+ 89	4
+ 414	4
+ 435	5
+ 517	3
+ 815	7
+ 687	1
+ 313	10
+ 116	9
+ 34	3
+ 255	1
+ 71	7
+ 12	4
+ 237	0
+ 812	1
+ 401	1
+ 505	5
+ 496	9
+ 893	9
+ 417	4
+ 193	2
+ 125	9
+ 321	4
+ 871	4
+ 380	9
+ 753	6
+ 53	8
+ 366	1
+ 264	6
+ 88	1
+ 747	5
+ 213	3
+ 979	7
+ 171	9
+ 640	6
+ 281	8
+ 819	4
+ 714	1
+ 845	6
+ 577	2
+ 489	3
+ 859	5
+ 154	2
+ 607	4
+ 828	7
+ 495	6
+ 184	7
+ 827	2
+ 417	10
+ 34	1
+ 586	3
+ 890	4
+ 721	6
+ 545	6
+ 188	1
+ 791	7
+ 452	7
+ 219	6
+ 875	8
+ 25	7
+ 521	5
+ 279	7
+ 228	1
+ 868	6
+ 105	9
+ 701	7
+ 217	6
+ 96	9
+ 196	6
+ 505	4
+ 763	3
+ 61	2
+ 946	3
+ 823	8
+ 107	8
+ 525	6
+ 368	8
+ 333	6
+ 910	2
+ 240	0
+ 103	9
+ 706	3
+ 533	8
+ 258	7
+ 443	8
+ 112	2
+ 58	2
+ 423	0
+ 455	2
+ 825	6
+ 93	4
+ 190	5
+ 154	5
+ 56	1
+ 725	3
+ 79	8
+ 237	8
+ 147	8
+ 587	4
+ 498	0
+ 167	6
+ 236	2
+ 785	7
+ 230	2
+ 904	1
+ 801	10
+ 405	10
+ 458	6
+ 515	5
+ 623	2
+ 810	7
+ 67	0
+ 486	2
+ 817	1
+ 619	3
+ 102	8
+ 926	3
+ 11	7
+ 998	2
+ 950	9
+ 297	8
+ 899	7
+ 743	4
+ 261	3
+ 871	9
+ 498	7
+ 585	6
+ 728	1
+ 779	5
+ 144	4
+ 861	2
+ 183	8
+ 585	2
+ 498	6
+ 436	4
+ 485	7
+ 200	4
+ 434	9
+ 741	7
+ 202	6
+ 578	7
+ 293	2
+ 264	0
+ 234	0
+ 566	4
+ 440	4
+ 624	6
+ 213	2
+ 817	7
+ 791	3
+ 160	3
+ 984	4
+ 660	4
+ 303	4
+ 113	5
+ 13	7
+ 204	3
+ 855	5
+ 326	1
+ 511	9
+ 467	10
+ 318	1
+ 573	5
+ 300	4
+ 242	1
+ 642	4
+ 367	6
+ 762	0
+ 45	1
+ 428	2
+ 570	4
+ 851	8
+ 746	7
+ 243	1
+ 795	8
+ 963	3
+ 705	3
+ 354	3
+ 811	7
+ 668	1
+ 744	3
+ 456	1
+ 937	2
+ 137	10
+ 283	6
+ 140	9
+ 5	10
+ 628	8
+ 697	9
+ 823	5
+ 626	8
+ 755	3
+ 66	1
+ 609	9
+ 762	3
+ 931	5
+ 586	4
+ 616	5
+ 604	8
+ 505	9
+ 318	6
+ 741	3
+ 636	4
+ 74	3
+ 241	9
+ 825	9
+ 683	6
+ 197	7
+ 688	8
+ 626	5
+ 82	6
+ 956	7
+ 944	6
+ 192	5
+ 325	7
+ 436	6
+ 342	2
+ 965	10
+ 547	0
+ 312	8
+ 936	1
+ 654	6
+ 717	9
+ 368	4
+ 658	10
+ 855	7
+ 551	8
+ 409	5
+ 382	6
+ 44	7
+ 298	5
+ 349	6
+ 658	3
+ 619	2
+ 354	9
+ 992	3
+ 67	6
+ 909	8
+ 498	3
+ 188	2
+ 271	0
+ 895	8
+ 854	3
+ 318	2
+ 905	4
+ 943	2
+ 843	3
+ 843	5
+ 607	5
+ 705	10
+ 392	7
+ 251	5
+ 343	2
+ 242	8
+ 437	4
+ 995	7
+ 474	9
+ 530	3
+ 195	8
+ 565	1
+ 210	5
+ 303	1
+ 800	1
+ 553	4
+ 609	3
+ 368	0
+ 955	6
+ 460	3
+ 780	7
+ 138	2
+ 133	1
+ 926	6
+ 24	5
+ 935	2
+ 304	5
+ 318	5
+ 8	6
+ 568	8
+ 768	1
+ 216	4
+ 379	6
+ 377	3
+ 204	8
+ 631	10
+ 539	8
+ 202	7
+ 901	1
+ 279	9
+ 584	2
+ 143	9
+ 714	5
+ 403	7
+ 83	10
+ 530	9
+ 92	7
+ 228	5
+ 331	6
+ 804	5
+ 442	4
+ 520	10
+ 203	7
+ 652	1
+ 850	9
+ 29	4
+ 145	2
+ 323	9
+ 633	7
+ 581	7
+ 696	1
+ 567	8
+ 857	8
+ 259	2
+ 400	1
+ 723	8
+ 498	2
+ 822	7
+ 965	5
+ 804	8
+ 405	8
+ 249	6
+ 5	6
+ 409	6
+ 297	10
+ 354	10
+ 100	9
+ 782	10
+ 716	0
+ 146	1
+ 104	9
+ 958	6
+ 112	8
+ 302	1
+ 255	1
+ 892	7
+ 939	1
+ 211	9
+ 713	6
+ 582	0
+ 609	9
+ 4	7
+ 857	8
+ 667	6
+ 828	8
+ 690	9
+ 683	6
+ 533	8
+ 428	8
+ 873	7
+ 942	8
+ 344	9
+ 907	6
+ 825	6
+ 174	4
+ 630	8
+ 343	6
+ 492	2
+ 421	2
+ 774	2
+ 972	5
+ 180	7
+ 112	7
+ 450	5
+ 549	3
+ 224	5
+ 88	6
+ 372	10
+ 122	2
+ 614	3
+ 604	2
+ 77	9
+ 880	6
+ 148	3
+ 728	9
+ 550	7
+ 386	7
+ 354	5
+ 444	8
+ 38	10
+ 127	3
+ 484	2
+ 829	9
+ 209	10
+ 53	8
+ 245	7
+ 68	3
+ 605	9
+ 892	8
+ 249	6
+ 674	8
+ 319	1
+ 529	7
+ 558	10
+ 478	6
+ 967	6
+ 858	5
+ 820	7
+ 307	0
+ 637	4
+ 852	9
+ 19	9
+ 205	6
+ 869	1
+ 376	1
+ 717	1
+ 917	0
+ 111	4
+ 709	7
+ 420	2
+ 265	4
+ 792	1
+ 838	6
+ 809	1
+ 640	4
+ 506	5
+ 328	5
+ 414	5
+ 148	3
+ 630	5
+ 400	3
+ 576	3
+ 382	7
+ 764	1
+ 356	2
+ 279	6
+ 571	1
+ 743	4
+ 683	6
+ 554	3
+ 998	1
+ 816	3
+ 585	2
+ 858	7
+ 512	5
+ 258	9
+ 835	8
+ 230	2
+ 520	10
+ 308	9
+ 177	6
+ 497	7
+ 659	2
+ 157	3
+ 793	7
+ 665	8
+ 772	5
+ 116	4
+ 711	10
+ 90	2
+ 463	3
+ 136	3
+ 181	4
+ 514	7
+ 359	8
+ 577	5
+ 410	1
+ 285	1
+ 314	4
+ 411	1
+ 153	1
+ 897	9
+ 557	0
+ 281	3
+ 988	4
+ 492	5
+ 719	6
+ 748	9
+ 993	3
+ 601	4
+ 85	2
+ 889	5
+ 251	2
+ 564	6
+ 616	10
+ 672	8
+ 50	6
+ 694	6
+ 582	10
+ 875	6
+ 346	4
+ 21	1
+ 994	8
+ 964	10
+ 31	6
+ 340	1
+ 742	2
+ 610	10
+ 402	2
+ 559	0
+ 148	2
+ 786	2
+ 800	5
+ 805	4
+ 455	7
+ 952	8
+ 48	10
+ 866	0
+ 741	8
+ 29	8
+ 395	4
+ 887	1
+ 597	5
+ 132	10
+ 670	7
+ 17	8
+ 921	8
+ 17	7
+ 283	8
+ 103	7
+ 503	1
+ 541	6
+ 27	4
+ 592	8
+ 238	6
+ 539	6
+ 990	4
+ 771	6
+ 922	9
+ 586	6
+ 593	6
+ 411	5
+ 406	4
+ 235	7
+ 250	3
+ 428	8
+ 393	10
+ 303	4
+ 376	9
+ 188	6
+ 516	7
+ 246	5
+ 153	0
+ 93	1
+ 919	7
+ 667	5
+ 282	1
+ 27	7
+ 506	3
+ 378	8
+ 600	8
+ 509	10
+ 775	8
+ 414	2
+ 707	6
+ 765	2
+ 328	0
+ 729	5
+ 28	8
+ 556	9
+ 501	2
+ 460	8
+ 301	5
+ 471	8
+ 749	8
+ 563	3
+ 655	1
+ 342	4
+ 883	8
+ 582	6
+ 357	3
+ 813	7
+ 357	5
+ 166	4
+ 364	7
+ 333	9
+ 945	8
+ 649	2
+ 280	1
+ 53	0
+ 969	6
+ 377	6
+ 688	7
+ 55	6
+ 476	6
+ 161	8
+ 983	10
+ 519	3
+ 516	7
+ 726	9
+ 407	1
+ 745	4
+ 853	4
+ 598	1
+ 514	7
+ 161	5
+ 268	5
+ 107	10
+ 258	2
+ 527	7
+ 799	7
+ 567	8
+ 663	1
+ 123	2
+ 772	8
+ 59	2
+ 909	8
+ 532	8
+ 197	1
+ 894	7
+ 781	1
+ 193	0
+ 593	3
+ 5	9
+ 463	5
+ 585	3
+ 221	2
+ 45	9
+ 238	2
+ 63	0
+ 18	1
+ 189	9
+ 925	7
+ 688	1
+ 851	6
+ 833	6
+ 636	0
+ 681	2
+ 327	7
+ 80	8
+ 217	7
+ 53	4
+ 817	1
+ 322	1
+ 266	4
+ 66	3
+ 506	3
+ 210	4
+ 976	9
+ 554	8
+ 480	4
+ 458	1
+ 414	1
+ 345	7
+ 824	4
+ 532	0
+ 90	6
+ 480	9
+ 683	8
+ 963	9
+ 187	0
+ 234	7
+ 284	4
+ 124	3
+ 342	7
+ 87	8
+ 66	5
+ 938	5
+ 683	3
+ 221	5
+ 708	8
+ 548	8
+ 338	0
+ 706	0
+ 830	7
+ 971	0
+ 699	2
+ 709	10
+ 649	8
+ 244	10
+ 511	3
+ 813	6
+ 875	8
+ 57	6
+ 34	3
+ 66	7
+ 30	6
+ 541	4
+ 642	2
+ 390	5
+ 917	4
+ 487	6
+ 565	2
+ 600	2
+ 29	8
+ 205	5
+ 174	0
+ 118	0
+ 769	2
+ 608	8
+ 452	7
+ 545	5
+ 288	1
+ 851	9
+ 333	2
+ 401	3
+ 601	9
+ 867	2
+ 84	5
+ 380	1
+ 311	6
+ 654	5
+ 604	8
+ 535	4
+ 947	1
+ 176	4
+ 817	7
+ 881	1
+ 808	7
+ 34	1
+ 972	4
+ 392	6
+ 322	3
+ 740	4
+ 725	1
+ 520	0
+ 706	2
+ 521	3
+ 947	1
+ 683	9
+ 199	9
+ 292	0
+ 581	2
+ 120	4
+ 905	2
+ 529	9
+ 588	9
+ 450	9
+ 179	2
+ 318	9
+ 310	8
+ 941	0
+ 13	5
+ 325	10
+ 518	0
+ 853	7
+ 867	1
+ 732	4
+ 318	9
+ 836	2
+ 6	4
+ 100	6
+ 287	6
+ 505	5
+ 741	8
+ 370	1
+ 661	3
+ 67	7
+ 773	4
+ 633	3
+ 401	5
+ 7	3
+ 631	7
+ 716	9
+ 592	6
+ 173	6
+ 917	3
+ 192	2
+ 824	7
+ 670	6
+ 520	0
+ 616	2
+ 351	7
+ 854	1
+ 75	5
+ 414	5
+ 973	4
+ 744	6
+ 160	5
+ 554	8
+ 11	7
+ 349	9
+ 0	5
+ 132	8
+ 239	8
+ 389	8
+ 842	0
+ 941	2
+ 688	8
+ 317	8
+ 282	7
+ 239	3
+ 151	10
+ 860	3
+ 441	4
+ 62	5
+ 141	4
+ 381	1
+ 952	5
+ 965	2
+ 315	4
+ 950	2
+ 359	9
+ 351	0
+ 686	7
+ 809	10
+ 397	0
+ 224	5
+ 30	1
+ 859	5
+ 497	9
+ 924	6
+ 331	3
+ 779	3
+ 818	7
+ 474	1
+ 99	4
+ 291	5
+ 316	6
+ 504	0
+ 308	3
+ 970	7
+ 361	2
+ 254	4
+ 277	1
+ 863	8
+ 33	8
+ 413	4
+ 93	2
+ 648	9
+ 937	1
+ 44	0
+ 546	3
+ 493	9
+ 976	10
+ 863	3
+ 310	8
+ 991	7
+ 27	2
+ 63	3
+ 358	9
+ 78	4
+ 714	5
+ 755	8
+ 682	4
+ 717	6
+ 525	8
+ 654	1
+ 97	1
+ 933	1
+ 144	8
+ 358	5
+ 629	3
+ 126	7
+ 593	2
+ 959	10
+ 115	0
+ 342	8
+ 527	1
+ 635	2
+ 501	4
+ 828	0
+ 114	5
+ 96	2
+ 630	0
+ 284	8
+ 825	6
+ 228	5
+ 990	4
+ 109	6
+ 542	1
+ 534	7
+ 105	9
+ 485	6
+ 974	1
+ 842	5
+ 473	7
+ 500	6
+ 152	6
+ 798	8
+ 625	1
+ 555	4
+ 723	8
+ 903	7
+ 136	0
+ 296	7
+ 80	8
+ 334	2
+ 706	8
+ 818	7
+ 940	7
+ 154	4
+ 329	7
+ 1000	5
+ 249	8
+ 264	9
+ 879	8
+ 323	6
+ 602	2
+ 314	7
+ 239	6
+ 416	3
+ 439	7
+ 505	1
+ 569	3
+ 825	5
+ 982	10
+ 921	3
+ 633	9
+ 793	9
+ 718	1
+ 756	6
+ 877	1
+ 198	5
+ 306	5
+ 217	5
+ 121	6
+ 864	6
+ 382	4
+ 706	10
+ 691	5
+ 460	7
+ 511	4
+ 985	1
+ 302	8
+ 26	0
+ 835	8
+ 617	7
+ 862	8
+ 191	2
+ 326	4
+ 713	4
+ 41	6
+ 8	4
+ 946	7
+ 375	6
+ 245	8
+ 310	8
+ 216	3
+ 900	5
+ 73	9
+ 538	9
+ 708	2
+ 620	6
+ 970	8
+ 738	3
+ 219	5
+ 743	3
+ 28	8
+ 683	10
+ 465	1
+ 611	7
+ 893	9
+ 466	1
+ 215	4
+ 626	3
+ 291	2
+ 196	10
+ 319	8
+ 569	3
+ 627	3
+ 585	8
+ 758	3
+ 107	8
+ 80	8
+ 759	5
+ 848	4
+ 255	7
+ 291	7
+ 849	5
+ 87	5
+ 794	4
+ 640	10
+ 378	10
+ 807	9
+ 249	4
+ 253	8
+ 281	0
+ 162	4
+ 797	2
+ 177	6
+ 787	0
+ 926	0
+ 766	2
+ 763	6
+ 723	9
+ 92	5
+ 228	7
+ 507	6
+ 692	3
+ 552	9
+ 748	8
+ 775	0
+ 817	9
+ 417	6
+ 179	6
+ 170	10
+ 619	1
+ 7	4
+ 312	8
+ 1	0
+ 621	1
+ 552	8
+ 825	1
+ 455	5
+ 373	0
+ 457	1
+ 813	2
+ 151	6
+ 169	6
+ 243	3
+ 161	4
+ 314	8
+ 508	3
+ 166	8
+ 92	2
+ 856	7
+ 259	4
+ 561	1
+ 467	0
+ 600	8
+ 24	1
+ 961	8
+ 289	1
+ 467	5
+ 679	7
+ 806	8
+ 124	1
+ 621	6
+ 441	8
+ 453	5
+ 954	3
+ 245	2
+ 716	8
+ 297	2
+ 823	9
+ 22	8
+ 955	10
+ 684	2
+ 95	2
+ 702	8
+ 862	5
+ 615	10
+ 628	2
+ 617	1
+ 23	1
+ 602	10
+ 378	8
+ 189	1
+ 654	5
+ 276	5
+ 383	3
+ 323	3
+ 281	0
+ 582	4
+ 159	3
+ 151	0
+ 793	8
+ 6	4
+ 2	6
+ 491	0
+ 693	1
+ 2	1
+ 941	2
+ 164	6
+ 677	4
+ 71	1
+ 737	4
+ 398	0
+ 402	10
+ 395	6
+ 264	5
+ 581	1
+ 312	6
+ 477	3
+ 209	10
+ 340	9
+ 61	3
+ 972	0
+ 532	1
+ 596	2
+ 576	7
+ 269	3
+ 61	7
+ 331	5
+ 647	7
+ 23	9
+ 272	6
+ 967	6
+ 190	4
+ 899	4
+ 413	2
+ 300	5
+ 581	3
+ 475	1
+ 408	1
+ 323	10
+ 738	6
+ 297	8
+ 259	6
+ 262	9
+ 354	3
+ 817	6
+ 890	8
+ 211	1
+ 230	1
+ 479	6
+ 350	8
+ 116	9
+ 52	6
+ 44	5
+ 662	4
+ 443	4
+ 959	7
+ 200	2
+ 368	5
+ 124	7
+ 748	9
+ 349	6
+ 727	6
+ 718	10
+ 671	2
+ 599	0
+ 977	7
+ 952	0
+ 307	10
+ 488	10
+ 363	9
+ 369	3
+ 672	6
+ 540	0
+ 31	7
+ 763	8
+ 606	1
+ 417	3
+ 672	1
+ 289	3
+ 333	9
+ 364	3
+ 604	3
+ 339	9
+ 311	8
+ 879	7
+ 759	2
+ 996	4
+ 817	5
+ 471	8
+ 199	2
+ 627	8
+ 346	0
+ 138	0
+ 180	4
+ 362	5
+ 316	7
+ 823	9
+ 41	2
+ 830	4
+ 989	7
+ 26	7
+ 957	0
+ 179	8
+ 557	7
+ 622	8
+ 885	2
+ 562	2
+ 294	7
+ 250	5
+ 128	6
+ 987	4
+ 337	8
+ 363	4
+ 972	2
+ 730	10
+ 901	8
+ 710	9
+ 777	9
+ 632	3
+ 540	3
+ 91	4
+ 503	7
+ 656	8
+ 353	9
+ 271	5
+ 517	3
+ 924	9
+ 68	3
+ 232	0
+ 480	10
+ 2	4
+ 717	7
+ 240	5
+ 600	9
+ 829	1
+ 126	9
+ 564	6
+ 573	2
+ 426	9
+ 126	7
+ 406	6
+ 955	3
+ 498	0
+ 618	7
+ 62	1
+ 692	1
+ 481	4
+ 775	7
+ 904	4
+ 592	7
+ 515	7
+ 652	1
+ 347	2
+ 300	8
+ 150	4
+ 471	6
+ 69	4
+ 887	6
+ 448	5
+ 297	5
+ 605	10
+ 574	1
+ 398	3
+ 806	3
+ 725	4
+ 34	2
+ 116	7
+ 320	5
+ 911	6
+ 237	1
+ 46	7
+ 619	1
+ 133	5
+ 682	6
+ 12	10
+ 91	6
+ 967	7
+ 702	4
+ 14	5
+ 667	7
+ 905	7
+ 978	0
+ 387	3
+ 484	3
+ 918	7
+ 361	10
+ 429	10
+ 78	6
+ 485	8
+ 143	5
+ 738	2
+ 113	7
+ 899	8
+ 70	9
+ 322	7
+ 651	2
+ 438	6
+ 247	8
+ 927	7
+ 124	8
+ 453	5
+ 808	9
+ 464	9
+ 445	9
+ 646	6
+ 446	4
+ 822	6
+ 89	7
+ 374	2
+ 633	7
+ 897	3
+ 923	3
+ 913	2
+ 160	8
+ 902	3
+ 684	4
+ 768	5
+ 237	2
+ 378	7
+ 181	0
+ 270	6
+ 408	1
+ 187	5
+ 814	6
+ 657	4
+ 257	6
+ 731	2
+ 889	6
+ 350	0
+ 484	3
+ 333	2
+ 607	1
+ 661	8
+ 333	0
+ 527	5
+ 63	8
+ 142	5
+ 890	3
+ 968	7
+ 889	6
+ 151	1
+ 179	9
+ 325	1
+ 526	7
+ 116	0
+ 927	4
+ 178	5
+ 550	8
+ 379	9
+ 877	9
+ 398	9
+ 703	5
+ 410	6
+ 868	4
+ 297	8
+ 3	4
+ 903	2
+ 329	2
+ 250	9
+ 903	4
+ 865	8
+ 815	0
+ 366	4
+ 881	7
+ 248	8
+ 651	6
+ 698	4
+ 185	1
+ 947	1
+ 487	2
+ 810	5
+ 691	7
+ 672	0
+ 940	9
+ 875	8
+ 287	7
diff --git a/contrib/bloom/expected/bloom.out b/contrib/bloom/expected/bloom.out
new file mode 100644
index ...f7334fa
*** a/contrib/bloom/expected/bloom.out
--- b/contrib/bloom/expected/bloom.out
***************
*** 0 ****
--- 1,120 ----
+ CREATE EXTENSION bloom;
+ CREATE TABLE tst (
+ 	i	int4,
+ 	t	text
+ );
+ \copy tst from 'data/data'
+ CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+ SET enable_seqscan=on;
+ SET enable_bitmapscan=off;
+ SET enable_indexscan=off;
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16;
+                 QUERY PLAN                 
+ -------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: (i = 16)
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: (i = 16)
+ (5 rows)
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE t = '5';
+                 QUERY PLAN                 
+ -------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: (t = '5'::text)
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: (t = '5'::text)
+ (5 rows)
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+                         QUERY PLAN                        
+ ----------------------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: ((i = 16) AND (t = '5'::text))
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: ((i = 16) AND (t = '5'::text))
+ (5 rows)
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ VACUUM ANALYZE tst;
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ VACUUM FULL tst;
+ SELECT count(*) FROM tst WHERE i = 16;
+  count 
+ -------
+     14
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   1008
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+  count 
+ -------
+      4
+ (1 row)
+ 
+ RESET enable_seqscan;
+ RESET enable_bitmapscan;
+ RESET enable_indexscan;
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
new file mode 100644
index ...e5c7a86
*** a/contrib/bloom/sql/bloom.sql
--- b/contrib/bloom/sql/bloom.sql
***************
*** 0 ****
--- 1,46 ----
+ CREATE EXTENSION bloom;
+ 
+ CREATE TABLE tst (
+ 	i	int4,
+ 	t	text
+ );
+ 
+ \copy tst from 'data/data'
+ 
+ CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+ 
+ SET enable_seqscan=on;
+ SET enable_bitmapscan=off;
+ SET enable_indexscan=off;
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16;
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE t = '5';
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ VACUUM ANALYZE tst;
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ VACUUM FULL tst;
+ 
+ SELECT count(*) FROM tst WHERE i = 16;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 16 AND t = '5';
+ 
+ RESET enable_seqscan;
+ RESET enable_bitmapscan;
+ RESET enable_indexscan;
diff --git a/contrib/bloom/t/001_wal.pl b/contrib/bloom/t/001_wal.pl
new file mode 100644
index ...bbb398b
*** a/contrib/bloom/t/001_wal.pl
--- b/contrib/bloom/t/001_wal.pl
***************
*** 0 ****
--- 1,75 ----
+ # Test generic xlog record work for bloom index replication.
+ use strict;
+ use warnings;
+ use PostgresNode;
+ use TestLib;
+ use Test::More tests => 31;
+ 
+ my $node_master;
+ my $node_standby;
+ 
+ # Run few queries on both master and standby and check their results match.
+ sub test_index_replay
+ {
+ 	my ($test_name) = @_;
+ 
+ 	# Wait for standby to catch up
+ 	my $applname = $node_standby->name;
+ 	my $caughtup_query =
+ 		"SELECT pg_current_xlog_location() <= write_location FROM pg_stat_replication WHERE application_name = '$applname';";
+ 	$node_master->poll_query_until('postgres', $caughtup_query)
+ 	  or die "Timed out while waiting for standby 1 to catch up";
+ 
+ 	my $queries = qq(SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ SELECT * FROM tst WHERE i = 0;
+ SELECT * FROM tst WHERE i = 3;
+ SELECT * FROM tst WHERE t = 'b';
+ SELECT * FROM tst WHERE t = 'f';
+ SELECT * FROM tst WHERE i = 3 AND t = 'c';
+ SELECT * FROM tst WHERE i = 7 AND t = 'e';
+ );
+ 
+ 	# Run test queries and compare their result
+ 	my $master_result = $node_master->psql("postgres", $queries);
+ 	my $standby_result = $node_standby->psql("postgres", $queries);
+ 
+ 	is($master_result, $standby_result, "$test_name: query result matches");
+ }
+ 
+ # Initialize master node
+ $node_master = get_new_node('master');
+ $node_master->init(allows_streaming => 1);
+ $node_master->start;
+ my $backup_name = 'my_backup';
+ 
+ # Take backup
+ $node_master->backup($backup_name);
+ 
+ # Create streaming standby linking to master
+ $node_standby = get_new_node('standby');
+ $node_standby->init_from_backup($node_master, $backup_name,
+ 	has_streaming => 1);
+ $node_standby->start;
+ 
+ # Create some bloom index on master
+ $node_master->psql("postgres", "CREATE EXTENSION bloom;");
+ $node_master->psql("postgres", "CREATE TABLE tst (i int4, t text);");
+ $node_master->psql("postgres", "INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,100000) i;");
+ $node_master->psql("postgres", "CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);");
+ 
+ # Test that queries give same result
+ test_index_replay('initial');
+ 
+ # Run 10 cycles of table modification. Run test queries after each modification.
+ for (my $i = 1; $i <= 10; $i++)
+ {
+ 	$node_master->psql("postgres", "DELETE FROM tst WHERE i = $i;");
+ 	test_index_replay("delete $i");
+ 	$node_master->psql("postgres", "VACUUM tst;");
+ 	test_index_replay("vacuum $i");
+ 	my ($start, $end) = (100001 + ($i - 1) * 10000, 100000 + $i * 10000);
+ 	$node_master->psql("postgres", "INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series($start,$end) i;");
+ 	test_index_replay("insert $i");
+ }
diff --git a/doc/src/sgml/bloom.sgml b/doc/src/sgml/bloom.sgml
new file mode 100644
index ...c207e6d
*** a/doc/src/sgml/bloom.sgml
--- b/doc/src/sgml/bloom.sgml
***************
*** 0 ****
--- 1,218 ----
+ <!-- doc/src/sgml/bloom.sgml -->
+ 
+ <sect1 id="bloom" xreflabel="bloom">
+  <title>bloom</title>
+ 
+  <indexterm zone="bloom">
+   <primary>bloom</primary>
+  </indexterm>
+ 
+  <para>
+   <literal>bloom</> is a contrib which implements index access method.  It comes
+   as example of custom access methods and generic WAL records usage.  But it
+   is also useful itself.
+  </para>
+ 
+  <sect2>
+   <title>Introduction</title>
+ 
+   <para>
+    Implementation of
+    <ulink url="http://en.wikipedia.org/wiki/Bloom_filter">Bloom filter</ulink>
+    allows fast exclusion of non-candidate tuples.
+    Since signature is a lossy representation of all indexed attributes, 
+    search results should be rechecked using heap information. 
+    User can specify signature length (in uint16, default is 5) and the number of 
+    bits, which can be setted, per attribute (1 < colN < 2048).
+   </para>
+ 
+   <para>
+    This index is useful if table has many attributes and queries can include
+    their arbitary combinations.  Traditional <literal>btree</> index is faster
+    than bloom index, but it'd require too many indexes to support all possible 
+    queries, while one need only one bloom index.  Bloom index supports only 
+    equality comparison.  Since it's a signature file, not a tree, it always
+    should be readed fully, but sequentially, so index search performance is 
+    constant and doesn't depend on a query. 
+   </para>
+  </sect2>
+ 
+  <sect2>
+   <title>Parameters</title>
+ 
+   <para>
+    <literal>bloom</> indexes accept following parameters in <literal>WITH</>
+    clause.
+   </para>
+ 
+    <variablelist>
+    <varlistentry>
+     <term><literal>length</></term>
+     <listitem>
+      <para>
+       Length of signature in uint16 type values
+      </para>
+     </listitem>
+    </varlistentry>
+    </variablelist>
+    <variablelist>
+    <varlistentry>
+     <term><literal>col1 &mdash; col16</></term>
+     <listitem>
+      <para>
+       Number of bits for corresponding column
+      </para>
+     </listitem>
+    </varlistentry>
+    </variablelist>
+  </sect2>
+ 
+  <sect2>
+   <title>Examples</title>
+ 
+   <para>
+    Example of index definition is given below.
+   </para>
+ 
+ <programlisting>
+ CREATE INDEX bloomidx ON tbloom(i1,i2,i3) 
+        WITH (length=5, col1=2, col2=2, col3=4);
+ </programlisting>
+ 
+   <para>
+    Here, we create bloom index with signature length 80 bits and attributes
+    i1, i2  mapped to 2 bits, attribute i3 - to 4 bits.
+   </para>
+ 
+   <para>
+    Example of index definition and usage is given below.
+   </para>
+ 
+ <programlisting>
+ CREATE TABLE tbloom AS
+ SELECT
+     random()::int as i1,
+     random()::int as i2,
+     random()::int as i3,
+     random()::int as i4,
+     random()::int as i5,
+     random()::int as i6,
+     random()::int as i7,
+     random()::int as i8,
+     random()::int as i9,
+     random()::int as i10,
+     random()::int as i11,
+     random()::int as i12,
+     random()::int as i13
+ FROM
+     generate_series(1,1000);
+ CREATE INDEX bloomidx ON tbloom USING
+              bloom (i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12);
+ SELECT pg_relation_size('bloomidx');
+ CREATE index btree_idx ON tbloom(i1,i2,i3,i4,i5,i6,i7,i8,i9,i10,i11,i12);
+ SELECT pg_relation_size('btree_idx');
+ </programlisting>
+ 
+ <programlisting>
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                                    QUERY PLAN
+ -----------------------------------------------------------------------------------------------------------------
+  Bitmap Heap Scan on tbloom  (cost=1.50..5.52 rows=1 width=52) (actual time=0.057..0.057 rows=0 loops=1)
+    Recheck Cond: ((i2 = 20) AND (i10 = 15))
+    ->  Bitmap Index Scan on bloomidx  (cost=0.00..1.50 rows=1 width=0) (actual time=0.041..0.041 rows=9 loops=1)
+          Index Cond: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.081 ms
+ (5 rows)
+ </programlisting>
+ 
+   <para>
+    Seqscan is slow.
+   </para>
+ 
+ <programlisting>
+ =# SET enable_bitmapscan = off;
+ =# SET enable_indexscan = off;
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                             QUERY PLAN
+ --------------------------------------------------------------------------------------------------
+  Seq Scan on tbloom  (cost=0.00..25.00 rows=1 width=52) (actual time=0.162..0.162 rows=0 loops=1)
+    Filter: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.181 ms
+ (3 rows)
+ </programlisting>
+ 
+  <para>
+   Btree index will be not used for this query.
+  </para>
+ 
+ <programlisting>
+ =# DROP INDEX bloomidx;
+ =# CREATE INDEX btree_idx ON tbloom(i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12);
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                             QUERY PLAN
+ --------------------------------------------------------------------------------------------------
+  Seq Scan on tbloom (cost=0.00..25.00 rows=1 width=52) (actual time=0.210..0.210 rows=0 loops=1)
+    Filter: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.250 ms
+ (3 rows)
+ </programlisting>
+  </sect2>
+ 
+  <sect2>
+   <title>Opclass interface</title>
+ 
+   <para>
+    Bloom opclass interface is simple.  It requires 1 supporting function:
+    hash function for indexing datatype.  And it provides 1 search operator:
+    equality operator.  The example below shows <literal>opclass</> definition
+    for <literal>text</> datatype.
+   </para>
+ 
+ <programlisting>
+ CREATE OPERATOR CLASS text_ops
+ DEFAULT FOR TYPE text USING bloom AS
+     OPERATOR    1   =(text, text),
+     FUNCTION    1   hashtext(text);
+ </programlisting>
+  </sect2>
+ 
+  <sect2>
+   <title>Limitation</title>
+   <para>
+ 
+    <itemizedlist>
+     <listitem>
+      <para>
+       For now, only opclasses for <literal>int4</>, <literal>text</> comes
+       with contrib.  However, users may define more of them.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       Only <literal>=</literal> operator is supported for search now.  But it's
+       possible to add support of arrays with contains and intersection
+       operations in future.
+      </para>
+     </listitem>
+    </itemizedlist>
+   </para>
+  </sect2>
+ 
+  <sect2>
+   <title>Authors</title>
+ 
+   <para>
+    Teodor Sigaev <email>teodor@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+ 
+   <para>
+    Alexander Korotkov <email>a.korotkov@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+ 
+   <para>
+    Oleg Bartunov <email>obartunov@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+  </sect2>
+ 
+ </sect1>
diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
new file mode 100644
index 4e3f337..c8708ec
*** a/doc/src/sgml/contrib.sgml
--- b/doc/src/sgml/contrib.sgml
*************** CREATE EXTENSION <replaceable>module_nam
*** 105,110 ****
--- 105,111 ----
   &adminpack;
   &auth-delay;
   &auto-explain;
+  &bloom;
   &btree-gin;
   &btree-gist;
   &chkpass;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
new file mode 100644
index 30adece..7c73206
*** a/doc/src/sgml/filelist.sgml
--- b/doc/src/sgml/filelist.sgml
***************
*** 106,111 ****
--- 106,112 ----
  <!ENTITY adminpack       SYSTEM "adminpack.sgml">
  <!ENTITY auth-delay      SYSTEM "auth-delay.sgml">
  <!ENTITY auto-explain    SYSTEM "auto-explain.sgml">
+ <!ENTITY bloom           SYSTEM "bloom.sgml">
  <!ENTITY btree-gin       SYSTEM "btree-gin.sgml">
  <!ENTITY btree-gist      SYSTEM "btree-gist.sgml">
  <!ENTITY chkpass         SYSTEM "chkpass.sgml">
#71Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Alvaro Herrera (#69)
Re: WIP: Access method extendability

Alvaro Herrera wrote:

Teodor Sigaev wrote:

So, per patch status:
create-am
ready

Teodor agreed to me committing this one instead of him; thanks. I just
pushed it after some mostly cosmetic adjustments.

This was maybe too laconic. I actually changed the code a good bit.
Some of the changes:

* pg_dump support got changed to use selectDumpableAccessMethod to
compare the OID to FirstNormalOid rather than embedding "10000" in the
SQL query. This is in line with what we do for other
no-schema-qualified object types.

* I changed DROP ACCESS METHOD to use the generic DropStmt instead of
creating a new production. I find that most of the DropFooStmt
productions are useless -- we should remove them and instead merge
everything into DropStmt.

* I changed get_object_address to use get_object_address_unqualified,
just like all other object types which use no-schema-qualified object
names. This removes a screenful of code. I had to revert get_am_oid to
its original state instead of adding the "amtype" argument. I added
get_index_am_oid.

* In SGML docs (and psql help) I changed the "TYPE INDEX" literal with
"TYPE <replaceable>access_method_type</>".

* I moved OCLASS_AM to a more sensible place rather than just stuffing
it at the end of the list

* I removed more than half the includes in the new file amcmds; there
were not necessary.

* I changed this:

+                        errmsg("function %s must return type \"index_am_handler\"",
+                               NameListToString(handler_name)));

to this:

+                        errmsg("function %s must return type \"%s\"",
+                               NameListToString(handler_name),
+                               "index_am_handler")));

This eases the job of translators: 1) there's no point in presenting the
type name to translate, since it cannot be translated, and 2) all the
equivalent sentences share a single translation instead of needing a
dozen separate translations that only differ in a word that cannot be
translated anyway. In doing this I noticed that most other uses do not
do this, so I'm going to change them too.

.. Oh crap. I just noticed I forgot to update a comment in pg_dump's
getAccessMethods. And we're missing psql tab-complete support for the
new commands.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#72Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Alvaro Herrera (#71)
2 attachment(s)
Re: WIP: Access method extendability

On Thu, Mar 24, 2016 at 6:19 PM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:

.. Oh crap. I just noticed I forgot to update a comment in pg_dump's
getAccessMethods. And we're missing psql tab-complete support for the
new commands.

Attached patches fix both these issues.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

Attachments:

fix-pg_dump-comment.patchapplication/octet-stream; name=fix-pg_dump-comment.patchDownload
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
new file mode 100644
index ad4b4e5..77bf8ac
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
*************** getAccessMethods(Archive *fout, int *num
*** 4157,4166 ****
  	/* Make sure we are in proper schema */
  	selectSourceSchema(fout, "pg_catalog");
  
! 	/*
! 	 * Select only user-defined access methods assuming all built-in access
! 	 * methods have oid < 10000.
! 	 */
  	appendPQExpBuffer(query, "SELECT tableoid, oid, amname, amtype, "
  					  "amhandler::pg_catalog.regproc AS amhandler "
  					  "FROM pg_am");
--- 4157,4163 ----
  	/* Make sure we are in proper schema */
  	selectSourceSchema(fout, "pg_catalog");
  
! 	/* Select all access methods from pg_am table */
  	appendPQExpBuffer(query, "SELECT tableoid, oid, amname, amtype, "
  					  "amhandler::pg_catalog.regproc AS amhandler "
  					  "FROM pg_am");
fix-am-tab-complete.patchapplication/octet-stream; name=fix-am-tab-complete.patchDownload
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
new file mode 100644
index 6a81416..eb592bb
*** a/src/bin/psql/tab-complete.c
--- b/src/bin/psql/tab-complete.c
*************** typedef struct
*** 877,882 ****
--- 877,883 ----
  #define THING_NO_SHOW		(THING_NO_CREATE | THING_NO_DROP)
  
  static const pgsql_thing_t words_after_create[] = {
+ 	{"ACCESS METHOD", NULL, NULL},
  	{"AGGREGATE", NULL, &Query_for_list_of_aggregates},
  	{"CAST", NULL, NULL},		/* Casts have complex structures for names, so
  								 * skip it */
*************** psql_completion(const char *text, int st
*** 1977,1982 ****
--- 1978,1994 ----
  		COMPLETE_WITH_LIST5("HEADER", "QUOTE", "ESCAPE", "FORCE QUOTE",
  							"FORCE NOT NULL");
  
+ 	/* CREATE ACCESS METHOD */
+ 	/* Complete "CREATE ACCESS METHOD <name>" */
+ 	else if (Matches4("CREATE", "ACCESS", "METHOD", MatchAny))
+ 		COMPLETE_WITH_CONST("TYPE");
+ 	/* Complete "CREATE ACCESS METHOD <name> TYPE" */
+ 	else if (Matches5("CREATE", "ACCESS", "METHOD", MatchAny, "TYPE"))
+ 		COMPLETE_WITH_CONST("INDEX");
+ 	/* Complete "CREATE ACCESS METHOD <name> TYPE <type>" */
+ 	else if (Matches6("CREATE", "ACCESS", "METHOD", MatchAny, "TYPE", MatchAny))
+ 		COMPLETE_WITH_CONST("HANDLER");
+ 
  	/* CREATE DATABASE */
  	else if (Matches3("CREATE", "DATABASE", MatchAny))
  		COMPLETE_WITH_LIST9("OWNER", "TEMPLATE", "ENCODING", "TABLESPACE",
*************** psql_completion(const char *text, int st
*** 2263,2268 ****
--- 2275,2281 ----
  	else if (Matches3("DROP",
  					  "COLLATION|CONVERSION|DOMAIN|EXTENSION|LANGUAGE|SCHEMA|SEQUENCE|SERVER|TABLE|TYPE|VIEW",
  					  MatchAny) ||
+ 			 Matches4("DROP", "ACCESS", "METHOD", MatchAny) ||
  			 (Matches4("DROP", "AGGREGATE|FUNCTION", MatchAny, MatchAny) &&
  			  ends_with(prev_wd, ')')) ||
  			 Matches4("DROP", "EVENT", "TRIGGER", MatchAny) ||
#73Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Petr Jelinek (#58)
Re: WIP: Access method extendability

Hi, Petr!

On Thu, Mar 17, 2016 at 10:56 AM, Petr Jelinek <petr@2ndquadrant.com> wrote:

On 16/03/16 15:31, Teodor Sigaev wrote:

Good catch, thanks! Tests were added.

I don't see any objection, is consensus reached? I'm close to comiit
that...

I did only cursory review on the bloom contrib module and don't really
have complaints there, but I know you can review that one. I'd like the
English of the generic_xlog.c description improved but I won't get to it
before weekend.

What is your plans about English of the generic_xlog.c?

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#74Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Alexander Korotkov (#72)
Re: WIP: Access method extendability

Alexander Korotkov wrote:

On Thu, Mar 24, 2016 at 6:19 PM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:

.. Oh crap. I just noticed I forgot to update a comment in pg_dump's
getAccessMethods. And we're missing psql tab-complete support for the
new commands.

Attached patches fix both these issues.

I see Teodor just pushed these two fixes. Thanks!

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#75David Steele
david@pgmasters.net
In reply to: Alvaro Herrera (#74)
Re: WIP: Access method extendability

On 3/28/16 1:26 PM, Alvaro Herrera wrote:

Alexander Korotkov wrote:

On Thu, Mar 24, 2016 at 6:19 PM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:

.. Oh crap. I just noticed I forgot to update a comment in pg_dump's
getAccessMethods. And we're missing psql tab-complete support for the
new commands.

Attached patches fix both these issues.

I see Teodor just pushed these two fixes. Thanks!

Does that mean this patch should be closed or is there more remaining to
commit?

Thanks,
--
-David
david@pgmasters.net

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#76Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: David Steele (#75)
Re: WIP: Access method extendability

On Tue, Mar 29, 2016 at 6:20 PM, David Steele <david@pgmasters.net> wrote:

On 3/28/16 1:26 PM, Alvaro Herrera wrote:

Alexander Korotkov wrote:

On Thu, Mar 24, 2016 at 6:19 PM, Alvaro Herrera <
alvherre@2ndquadrant.com>
wrote:

.. Oh crap. I just noticed I forgot to update a comment in pg_dump's

getAccessMethods. And we're missing psql tab-complete support for the
new commands.

Attached patches fix both these issues.

I see Teodor just pushed these two fixes. Thanks!

Does that mean this patch should be closed or is there more remaining to
commit?

There are still two pending patches:
* Generic WAL records
* Bloom filter contrib

Teodor is going to commit them. But he is waiting Petr Jelinek to review
the English of the first one.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#77Teodor Sigaev
teodor@sigaev.ru
In reply to: David Steele (#75)
Re: WIP: Access method extendability

Does that mean this patch should be closed or is there more remaining to commit?

Petr promises to check english in comments/docs in generic-wal patch at this
week, bloom patch depends on it. Bloom patch is ready from my point of view.

--
Teodor Sigaev E-mail: teodor@sigaev.ru
WWW: http://www.sigaev.ru/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#78Petr Jelinek
petr@2ndquadrant.com
In reply to: Teodor Sigaev (#77)
1 attachment(s)
Re: WIP: Access method extendability

On 29/03/16 17:33, Teodor Sigaev wrote:

Does that mean this patch should be closed or is there more remaining
to commit?

Petr promises to check english in comments/docs in generic-wal patch at
this week, bloom patch depends on it. Bloom patch is ready from my point
of view.

And here it is. It's not perfect but it's better (I am not native
speaker either). It's same as v12, just changed comments somewhat.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

0002-generic-xlog.13.patchtext/plain; charset=UTF-8; name=0002-generic-xlog.13.patchDownload
diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
new file mode 100644
index c72a1f2..c0e38fd
*** a/src/backend/access/rmgrdesc/Makefile
--- b/src/backend/access/rmgrdesc/Makefile
*************** subdir = src/backend/access/rmgrdesc
*** 8,16 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o gindesc.o gistdesc.o \
! 	   hashdesc.o heapdesc.o mxactdesc.o nbtdesc.o relmapdesc.o \
! 	   replorigindesc.o seqdesc.o smgrdesc.o spgdesc.o \
  	   standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
  
  include $(top_srcdir)/src/backend/common.mk
--- 8,16 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o genericdesc.o \
! 	   gindesc.o gistdesc.o hashdesc.o heapdesc.o mxactdesc.o nbtdesc.o \
! 	   relmapdesc.o replorigindesc.o seqdesc.o smgrdesc.o spgdesc.o \
  	   standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
  
  include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/genericdesc.c b/src/backend/access/rmgrdesc/genericdesc.c
new file mode 100644
index ...3d035c2
*** a/src/backend/access/rmgrdesc/genericdesc.c
--- b/src/backend/access/rmgrdesc/genericdesc.c
***************
*** 0 ****
--- 1,58 ----
+ /*-------------------------------------------------------------------------
+  *
+  * genericdesc.c
+  *	  rmgr descriptor routines for access/transam/generic_xlog.c
+  *
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/backend/access/rmgrdesc/genericdesc.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/generic_xlog.h"
+ #include "lib/stringinfo.h"
+ #include "storage/relfilenode.h"
+ 
+ /*
+  * Description of generic xlog record: write page regions which this record
+  * overrides.
+  */
+ void
+ generic_desc(StringInfo buf, XLogReaderState *record)
+ {
+ 	Pointer		ptr = XLogRecGetData(record),
+ 				end = ptr + XLogRecGetDataLen(record);
+ 
+ 	while (ptr < end)
+ 	{
+ 		OffsetNumber	offset,
+ 						length;
+ 
+ 		memcpy(&offset, ptr, sizeof(offset));
+ 		ptr += sizeof(offset);
+ 		memcpy(&length, ptr, sizeof(length));
+ 		ptr += sizeof(length);
+ 		ptr += length;
+ 
+ 		if (ptr < end)
+ 			appendStringInfo(buf, "offset %u, length %u; ", offset, length);
+ 		else
+ 			appendStringInfo(buf, "offset %u, length %u", offset, length);
+ 	}
+ 
+ 	return;
+ }
+ 
+ /*
+  * Identification of generic xlog record: we don't distinguish any subtypes
+  * inside generic xlog records.
+  */
+ const char *
+ generic_identify(uint8 info)
+ {
+ 	return "Generic";
+ }
diff --git a/src/backend/access/transam/Makefile b/src/backend/access/transam/Makefile
new file mode 100644
index 94455b2..16fbe47
*** a/src/backend/access/transam/Makefile
--- b/src/backend/access/transam/Makefile
*************** subdir = src/backend/access/transam
*** 12,19 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = clog.o commit_ts.o multixact.o parallel.o rmgr.o slru.o subtrans.o \
! 	timeline.o transam.o twophase.o twophase_rmgr.o varsup.o \
  	xact.o xlog.o xlogarchive.o xlogfuncs.o \
  	xloginsert.o xlogreader.o xlogutils.o
  
--- 12,19 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = clog.o commit_ts.o generic_xlog.o multixact.o parallel.o rmgr.o slru.o \
! 	subtrans.o timeline.o transam.o twophase.o twophase_rmgr.o varsup.o \
  	xact.o xlog.o xlogarchive.o xlogfuncs.o \
  	xloginsert.o xlogreader.o xlogutils.o
  
diff --git a/src/backend/access/transam/generic_xlog.c b/src/backend/access/transam/generic_xlog.c
new file mode 100644
index ...7ca03bf
*** a/src/backend/access/transam/generic_xlog.c
--- b/src/backend/access/transam/generic_xlog.c
***************
*** 0 ****
--- 1,510 ----
+ /*-------------------------------------------------------------------------
+  *
+  * generic_xlog.c
+  *	 Implementation of generic xlog records.
+  *
+  *
+  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/backend/access/transam/generic_xlog.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/generic_xlog.h"
+ #include "access/xlogutils.h"
+ #include "miscadmin.h"
+ #include "utils/memutils.h"
+ 
+ /*-------------------------------------------------------------------------
+  * API for construction of generic xlog records
+  *
+  * This API allows user to construct generic xlog records which are
+  * describing difference between pages in a general way.  Thus it's useful
+  * for extensions which provide custom access methods because they can't
+  * register their own WAL redo routines.
+  *
+  * Generic xlog record should be constructed in following steps.
+  * 1) GenericXLogStart(relation) - start construction of generic xlog
+  *	  record for given relation.
+  * 2) GenericXLogRegister(buffer, isNew) - register one or more buffers
+  *	  for generic xlog record.  This function returns a copy of the page
+  *	  image where modifications can be performed.  The second argument
+  *	  indicates if block is new and full image should be taken.
+  * 3) Do modification of page images obtained in previous step.
+  * 4) GenericXLogFinish() - finish construction of generic xlog record.
+  *
+  * Please, note the following points when constructing generic xlog records.
+  * - No direct modifications of page images are allowed! All modifications
+  *	 must be done in copies returned by GenericXLogRegister().  In other
+  *	 words the code which makes generic xlog records should never call
+  *	 BufferGetPage() function.
+  * - The xlog record construction can be canceled at any step by calling
+  *	 GenericXLogAbort().  All changes made to page images copies will be
+  *	 discarded.
+  * - Registrations of buffers (step 2) and modifications of page images
+  *	 (step 3) can be mixed in any sequence.  The only restriction is that
+  *	 you can only modify page image after registration of corresponding
+  *	 buffer.
+  * - After registration, the buffer also can be unregistered by calling
+  *	 GenericXLogUnregister(buffer).  In this case the changes made in
+  *	 particular page image copy will be discarded.
+  * - Generic xlog assumes that pages are using standard layout.  I.e. all
+  *	 information between pd_lower and pd_upper will be discarded.
+  * - Maximum number of buffers simultaneously registered for generic xlog
+  *	 is MAX_GENERIC_XLOG_PAGES.  Error will be thrown if this limit
+  *	 exceeded.
+  * - Since you modify copies of page images, GenericXLogStart() doesn't
+  *	 start a critical section.  Thus, you can do memory allocation, error
+  *	 throwing etc between GenericXLogStart() and GenericXLogFinish().
+  *	 Actual critical section is present inside GenericXLogFinish().
+  * - GenericXLogFinish() takes care of marking buffers dirty and setting their
+  *	 LSNs.  You don't need to do this explicitly.
+  * - For unlogged relations, everything works the same expect there is no
+  *	 WAL record produced.  Thus, you typically don't need to do any explicit
+  *	 checks for unlogged relations.
+  * - If registered buffer isn't new, generic xlog record contains delta
+  *	 between old and new page images.  This delta is produced by per byte
+  *	 comparison.  Current delta mechanist is not effective for data shift
+  *	 inside the page.  However, it could be improved in further versions.
+  * - Generic xlog redo function will acquire exclusive locks to buffers
+  *	 in the same order they were registered.  After redo of all changes
+  *	 locks will be released in the same order.
+  *
+  * Internally, delta between pages consists of set of fragments.  Each
+  * fragment represents changes made in given region of page.  Fragment is
+  * described as following.
+  *
+  * - offset of page region (OffsetNumber)
+  * - length of page region (OffsetNumber)
+  * - data - the data to place into described region ('length' number of bytes)
+  *
+  * Unchanged regions of page are not represented in the dekta.  As a result
+  * delta can be more compact than full page image.  But if unchanged region
+  * of the page is less than fragment header (offset and length) the delta
+  * would be bigger than the full page image. For this reason we break fragment
+  * only if the unchanged region is bigger than MATCH_THRESHOLD.
+  *
+  * The worst case for delta size is when we didn't find any unchanged region
+  * in the page. Then size of delta would be size of page plus size of fragment
+  * header.
+  */
+ #define FRAGMENT_HEADER_SIZE	(2 * sizeof(OffsetNumber))
+ #define MATCH_THRESHOLD			FRAGMENT_HEADER_SIZE
+ #define MAX_DELTA_SIZE			BLCKSZ + FRAGMENT_HEADER_SIZE
+ 
+ /* Struct of generic xlog data for single page */
+ typedef struct
+ {
+ 	Buffer	buffer;			/* registered buffer */
+ 	char	image[BLCKSZ];	/* copy of page image for modification */
+ 	char	data[MAX_DELTA_SIZE]; /* delta between page images */
+ 	int		dataLen;		/* space consumed in data field */
+ 	bool	fullImage;		/* are we taking full image of this page? */
+ } PageData;
+ 
+ /* Enum of generic xlog (gxlog) status */
+ enum GenericXlogStatus
+ {
+ 	GXLOG_NOT_STARTED,	/* gxlog is not started */
+ 	GXLOG_LOGGED,		/* gxlog is started for logged relation */
+ 	GXLOG_UNLOGGED		/* gxlog is started for unlogged relation */
+ };
+ 
+ static enum GenericXlogStatus	genericXlogStatus = GXLOG_NOT_STARTED;
+ static PageData					pages[MAX_GENERIC_XLOG_PAGES];
+ 
+ 
+ static void writeFragment(PageData *pageData, OffsetNumber offset,
+ 						  OffsetNumber len, Pointer data);
+ static void writeDelta(PageData *pageData);
+ static void applyPageRedo(Page page, Pointer data, Size dataSize);
+ 
+ /*
+  * Write next fragment into delta.
+  */
+ static void
+ writeFragment(PageData *pageData, OffsetNumber offset, OffsetNumber length,
+ 			  Pointer data)
+ {
+ 	Pointer			ptr = pageData->data + pageData->dataLen;
+ 
+ 	/* Check we have enough of space */
+ 	Assert(pageData->dataLen + sizeof(offset) +
+ 		   sizeof(length) + length <= sizeof(pageData->data));
+ 
+ 	/* Write fragment data */
+ 	memcpy(ptr, &offset, sizeof(offset));
+ 	ptr += sizeof(offset);
+ 	memcpy(ptr, &length, sizeof(length));
+ 	ptr += sizeof(length);
+ 	memcpy(ptr, data, length);
+ 	ptr += length;
+ 
+ 	pageData->dataLen = ptr - pageData->data;
+ }
+ 
+ /*
+  * Make delta for given page.
+  */
+ static void
+ writeDelta(PageData *pageData)
+ {
+ 	Page			page = BufferGetPage(pageData->buffer),
+ 					image = (Page) pageData->image;
+ 	int				i,
+ 					fragmentBegin = -1,
+ 					fragmentEnd = -1;
+ 	uint16			pageLower = ((PageHeader) page)->pd_lower,
+ 					pageUpper = ((PageHeader) page)->pd_upper,
+ 					imageLower = ((PageHeader) image)->pd_lower,
+ 					imageUpper = ((PageHeader) image)->pd_upper;
+ 
+ 	for (i = 0; i < BLCKSZ; i++)
+ 	{
+ 		bool	match;
+ 
+ 		/*
+ 		 * Check if bytes in old and new page images matches.  We don't care
+ 		 * about data in unallocated area between pd_lower and pd_upper.  Thus
+ 		 * we assume unallocated area to expand with unmatched bytes.  Bytes
+ 		 * inside unallocated area are assumed to always match.
+ 		 */
+ 		if (i < pageLower)
+ 		{
+ 			if (i < imageLower)
+ 				match = (page[i] == image[i]);
+ 			else
+ 				match = false;
+ 		}
+ 		else if (i >= pageUpper)
+ 		{
+ 			if (i >= imageUpper)
+ 				match = (page[i] == image[i]);
+ 			else
+ 				match = false;
+ 		}
+ 		else
+ 		{
+ 			match = true;
+ 		}
+ 
+ 		if (match)
+ 		{
+ 			if (fragmentBegin >= 0)
+ 			{
+ 				/* Matched byte is potential of fragment. */
+ 				if (fragmentEnd < 0)
+ 					fragmentEnd = i;
+ 
+ 				/*
+ 				 * Write next fragment if sequence of matched bytes is longer
+ 				 * than MATCH_THRESHOLD.
+ 				 */
+ 				if (i - fragmentEnd >= MATCH_THRESHOLD)
+ 				{
+ 					writeFragment(pageData, fragmentBegin,
+ 								  fragmentEnd - fragmentBegin,
+ 								  page + fragmentBegin);
+ 					fragmentBegin = -1;
+ 					fragmentEnd = -1;
+ 				}
+ 			}
+ 		}
+ 		else
+ 		{
+ 			/* On unmatched byte, start new fragment if it's not done yet */
+ 			if (fragmentBegin < 0)
+ 				fragmentBegin = i;
+ 			fragmentEnd = -1;
+ 		}
+ 	}
+ 
+ 	if (fragmentBegin >= 0)
+ 		writeFragment(pageData, fragmentBegin,
+ 					  BLCKSZ - fragmentBegin,
+ 					  page + fragmentBegin);
+ 
+ #ifdef WAL_DEBUG
+ 	/*
+ 	 * If xlog debug is enabled then check produced delta.  Result of delta
+ 	 * application to saved image should be the same as current page state.
+ 	 */
+ 	if (XLOG_DEBUG)
+ 	{
+ 		char	tmp[BLCKSZ];
+ 		memcpy(tmp, image, BLCKSZ);
+ 		applyPageRedo(tmp, pageData->data, pageData->dataLen);
+ 		if (memcmp(tmp, page, pageLower)
+ 			|| memcmp(tmp + pageUpper, page + pageUpper, BLCKSZ - pageUpper))
+ 			elog(ERROR, "result of generic xlog apply doesn't match");
+ 	}
+ #endif
+ }
+ 
+ /*
+  * Start new generic xlog record.
+  */
+ void
+ GenericXLogStart(Relation relation)
+ {
+ 	int i;
+ 
+ 	if (genericXlogStatus != GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogStart: generic xlog is already started")));
+ 
+ 	genericXlogStatus = RelationNeedsWAL(relation) ? GXLOG_LOGGED : GXLOG_UNLOGGED;
+ 
+ 	for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 	{
+ 		pages[i].buffer = InvalidBuffer;
+ 	}
+ }
+ 
+ /*
+  * Register new buffer for generic xlog record.
+  */
+ Page
+ GenericXLogRegister(Buffer buffer, bool isNew)
+ {
+ 	int block_id;
+ 
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogRegister: generic xlog isn't started")));
+ 
+ 	/* Place new buffer to unused slot in array */
+ 	for (block_id = 0; block_id < MAX_GENERIC_XLOG_PAGES; block_id++)
+ 	{
+ 		if (BufferIsInvalid(pages[block_id].buffer))
+ 		{
+ 			pages[block_id].buffer = buffer;
+ 			memcpy(pages[block_id].image, BufferGetPage(buffer), BLCKSZ);
+ 			pages[block_id].dataLen = 0;
+ 			pages[block_id].fullImage = isNew;
+ 			return (Page)pages[block_id].image;
+ 		}
+ 		else if (pages[block_id].buffer == buffer)
+ 		{
+ 			/* 
+ 			 * Buffer already registered.  Just return image which is already
+ 			 * prepared.
+ 			 */
+ 			return (Page)pages[block_id].image;
+ 		}
+ 	}
+ 
+ 	ereport(ERROR,
+ 			(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ 			 errmsg("GenericXLogRegister: maximum number of %d buffers is exceeded",
+ 					MAX_GENERIC_XLOG_PAGES)));
+ 
+ 	/* keep compiler quiet */
+ 	return NULL;
+ }
+ 
+ /*
+  * Unregister particular buffer for generic xlog record.
+  */
+ void
+ GenericXLogUnregister(Buffer buffer)
+ {
+ 	int block_id;
+ 
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogUnregister: generic xlog isn't started")));
+ 
+ 	/* Find block in array to unregister */
+ 	for (block_id = 0; block_id < MAX_GENERIC_XLOG_PAGES; block_id++)
+ 	{
+ 		if (pages[block_id].buffer == buffer)
+ 		{
+ 			/*
+ 			 * Preserve order of pages in array because it could matter for
+ 			 * concurrency.
+ 			 */
+ 			memmove(&pages[block_id], &pages[block_id + 1],
+ 					(MAX_GENERIC_XLOG_PAGES - block_id - 1) * sizeof(PageData));
+ 			pages[MAX_GENERIC_XLOG_PAGES - 1].buffer = InvalidBuffer;
+ 			return;
+ 		}
+ 	}
+ 
+ 	ereport(ERROR,
+ 			(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 			 errmsg("GenericXLogUnregister: registered buffer not found")));
+ }
+ 
+ /*
+  * Put all changes in registered buffers to generic xlog record.
+  */
+ XLogRecPtr
+ GenericXLogFinish(void)
+ {
+ 	XLogRecPtr lsn = InvalidXLogRecPtr;
+ 	int i;
+ 
+ 	if (genericXlogStatus == GXLOG_LOGGED)
+ 	{
+ 		/* Logged relation: make xlog record in critical section. */
+ 		START_CRIT_SECTION();
+ 		XLogBeginInsert();
+ 
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			char	tmp[BLCKSZ];
+ 
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 
+ 			/* Swap current and saved page image. */
+ 			memcpy(tmp, pages[i].image, BLCKSZ);
+ 			memcpy(pages[i].image, BufferGetPage(pages[i].buffer), BLCKSZ);
+ 			memcpy(BufferGetPage(pages[i].buffer), tmp, BLCKSZ);
+ 
+ 			if (pages[i].fullImage)
+ 			{
+ 				/* Full page image doesn't require anything special from us */
+ 				XLogRegisterBuffer(i, pages[i].buffer, REGBUF_FORCE_IMAGE);
+ 			}
+ 			else
+ 			{
+ 				/*
+ 				 * In normal node calculate delta and write use it as data
+ 				 * associated with this page.
+ 				 */
+ 				XLogRegisterBuffer(i, pages[i].buffer, REGBUF_STANDARD);
+ 				writeDelta(&pages[i]);
+ 				XLogRegisterBufData(i, pages[i].data, pages[i].dataLen);
+ 			}
+ 		}
+ 
+ 		/* Insert xlog record */
+ 		lsn = XLogInsert(RM_GENERIC_ID, 0);
+ 
+ 		/* Set LSN and make buffers dirty */
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 			PageSetLSN(BufferGetPage(pages[i].buffer), lsn);
+ 			MarkBufferDirty(pages[i].buffer);
+ 		}
+ 		END_CRIT_SECTION();
+ 	}
+ 	else if (genericXlogStatus == GXLOG_UNLOGGED)
+ 	{
+ 		/* Unlogged relation: skip xlog-related stuff */
+ 		START_CRIT_SECTION();
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 			memcpy(BufferGetPage(pages[i].buffer), pages[i].image, BLCKSZ);
+ 			MarkBufferDirty(pages[i].buffer);
+ 		}
+ 		END_CRIT_SECTION();
+ 	}
+ 	else
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogFinish: generic xlog isn't started")));
+ 	}
+ 
+ 	genericXlogStatus = GXLOG_NOT_STARTED;
+ 
+ 	return lsn;
+ }
+ 
+ /*
+  * Abort generic xlog record.
+  */
+ void
+ GenericXLogAbort(void)
+ {
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogAbort: generic xlog isn't started")));
+ 
+ 	genericXlogStatus = GXLOG_NOT_STARTED;
+ }
+ 
+ /*
+  * Apply delta to given page image.
+  */
+ static void
+ applyPageRedo(Page page, Pointer data, Size dataSize)
+ {
+ 	Pointer ptr = data, end = data + dataSize;
+ 
+ 	while (ptr < end)
+ 	{
+ 		OffsetNumber	offset,
+ 						length;
+ 
+ 		memcpy(&offset, ptr, sizeof(offset));
+ 		ptr += sizeof(offset);
+ 		memcpy(&length, ptr, sizeof(length));
+ 		ptr += sizeof(length);
+ 
+ 		memcpy(page + offset, ptr, length);
+ 
+ 		ptr += length;
+ 	}
+ }
+ 
+ /*
+  * Redo function for generic xlog record.
+  */
+ void
+ generic_redo(XLogReaderState *record)
+ {
+ 	uint8		block_id;
+ 	Buffer		buffers[MAX_GENERIC_XLOG_PAGES] = {InvalidBuffer};
+ 	XLogRecPtr	lsn = record->EndRecPtr;
+ 
+ 	Assert(record->max_block_id < MAX_GENERIC_XLOG_PAGES);
+ 
+ 	/* Interate over blocks */
+ 	for (block_id = 0; block_id <= record->max_block_id; block_id++)
+ 	{
+ 		XLogRedoAction action;
+ 
+ 		if (!XLogRecHasBlockRef(record, block_id))
+ 			continue;
+ 
+ 		action = XLogReadBufferForRedo(record, block_id, &buffers[block_id]);
+ 
+ 		/* Apply redo to given block if needed */
+ 		if (action == BLK_NEEDS_REDO)
+ 		{
+ 			Pointer	blockData;
+ 			Size	blockDataSize;
+ 			Page	page;
+ 
+ 			page = BufferGetPage(buffers[block_id]);
+ 			blockData = XLogRecGetBlockData(record, block_id, &blockDataSize);
+ 			applyPageRedo(page, blockData, blockDataSize);
+ 
+ 			PageSetLSN(page, lsn);
+ 			MarkBufferDirty(buffers[block_id]);
+ 		}
+ 	}
+ 
+ 	/* Changes are done: unlock and release all buffers */
+ 	for (block_id = 0; block_id <= record->max_block_id; block_id++)
+ 	{
+ 		if (BufferIsValid(buffers[block_id]))
+ 			UnlockReleaseBuffer(buffers[block_id]);
+ 	}
+ }
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
new file mode 100644
index 7c4d773..7b38c16
*** a/src/backend/access/transam/rmgr.c
--- b/src/backend/access/transam/rmgr.c
***************
*** 11,16 ****
--- 11,17 ----
  #include "access/commit_ts.h"
  #include "access/gin.h"
  #include "access/gist_private.h"
+ #include "access/generic_xlog.h"
  #include "access/hash.h"
  #include "access/heapam_xlog.h"
  #include "access/brin_xlog.h"
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
new file mode 100644
index 13af485..262deb2
*** a/src/backend/replication/logical/decode.c
--- b/src/backend/replication/logical/decode.c
*************** LogicalDecodingProcessRecord(LogicalDeco
*** 143,148 ****
--- 143,149 ----
  		case RM_BRIN_ID:
  		case RM_COMMIT_TS_ID:
  		case RM_REPLORIGIN_ID:
+ 		case RM_GENERIC_ID:
  			/* just deal with xid, and done */
  			ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(record),
  									buf.origptr);
diff --git a/src/bin/pg_xlogdump/.gitignore b/src/bin/pg_xlogdump/.gitignore
new file mode 100644
index eebaf30..33a1acf
*** a/src/bin/pg_xlogdump/.gitignore
--- b/src/bin/pg_xlogdump/.gitignore
***************
*** 4,9 ****
--- 4,10 ----
  /clogdesc.c
  /committsdesc.c
  /dbasedesc.c
+ /genericdesc.c
  /gindesc.c
  /gistdesc.c
  /hashdesc.c
diff --git a/src/bin/pg_xlogdump/rmgrdesc.c b/src/bin/pg_xlogdump/rmgrdesc.c
new file mode 100644
index f9cd395..cff7e59
*** a/src/bin/pg_xlogdump/rmgrdesc.c
--- b/src/bin/pg_xlogdump/rmgrdesc.c
***************
*** 11,16 ****
--- 11,17 ----
  #include "access/brin_xlog.h"
  #include "access/clog.h"
  #include "access/commit_ts.h"
+ #include "access/generic_xlog.h"
  #include "access/gin.h"
  #include "access/gist_private.h"
  #include "access/hash.h"
diff --git a/src/include/access/generic_xlog.h b/src/include/access/generic_xlog.h
new file mode 100644
index ...49249e0
*** a/src/include/access/generic_xlog.h
--- b/src/include/access/generic_xlog.h
***************
*** 0 ****
--- 1,36 ----
+ /*-------------------------------------------------------------------------
+  *
+  * generic_xlog.h
+  *	  Generic xlog API definition.
+  *
+  *
+  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/access/generic_xlog.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef GENERIC_XLOG_H
+ #define GENERIC_XLOG_H
+ 
+ #include "access/xlog.h"
+ #include "access/xlog_internal.h"
+ #include "storage/bufpage.h"
+ #include "utils/rel.h"
+ 
+ #define MAX_GENERIC_XLOG_PAGES	  3
+ 
+ /* API for construction of generic xlog records */
+ extern void GenericXLogStart(Relation relation);
+ extern Page GenericXLogRegister(Buffer buffer, bool isNew);
+ extern void GenericXLogUnregister(Buffer buffer);
+ extern XLogRecPtr GenericXLogFinish(void);
+ extern void GenericXLogAbort(void);
+ 
+ /* functions defined for rmgr */
+ extern void generic_redo(XLogReaderState *record);
+ extern const char *generic_identify(uint8 info);
+ extern void generic_desc(StringInfo buf, XLogReaderState *record);
+ 
+ #endif   /* GENERIC_XLOG_H */
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
new file mode 100644
index fab912d..3cfe6f7
*** a/src/include/access/rmgrlist.h
--- b/src/include/access/rmgrlist.h
*************** PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo
*** 45,47 ****
--- 45,48 ----
  PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL)
  PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL)
  PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL)
+ PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL)
#79Teodor Sigaev
teodor@sigaev.ru
In reply to: Petr Jelinek (#78)
Re: WIP: Access method extendability

And here it is. It's not perfect but it's better (I am not native speaker
either). It's same as v12, just changed comments somewhat.

Thank you, but I have a problems with applying:

% patch -p1 < ~/Downloads/0002-generic-xlog.13.patch
Hmm... Looks like a new-style context diff to me...
...
|diff --git a/src/backend/access/transam/generic_xlog.c
b/src/backend/access/transam/generic_xlog.c
|new file mode 100644
|index ...7ca03bf
|*** a/src/backend/access/transam/generic_xlog.c
|--- b/src/backend/access/transam/generic_xlog.c
--------------------------
(Creating file src/backend/access/transam/generic_xlog.c...)
Patching file src/backend/access/transam/generic_xlog.c using Plan A...
patch: **** malformed patch at line 634: diff --git
a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c

--
Teodor Sigaev E-mail: teodor@sigaev.ru
WWW: http://www.sigaev.ru/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#80Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Petr Jelinek (#78)
Re: WIP: Access method extendability

Petr Jelinek wrote:

And here it is. It's not perfect but it's better (I am not native speaker
either). It's same as v12, just changed comments somewhat.

I think this can still be improved a bit more, in particular this large
comment, which I edit inline for expediency.

+ /*-------------------------------------------------------------------------
+  * API for construction of generic xlog records
+  *
+  * This API allows user to construct generic xlog records which describe
+  * difference between pages in a generic way.  This is useful for
+  * extensions which provide custom access methods because they can't
+  * register their own WAL redo routines.
+  *
+  * Each record must be constructed by following these steps:
+  * 1) GenericXLogStart(relation) - start construction of a generic xlog
+  *	  record for the given relation.
+  * 2) GenericXLogRegister(buffer, isNew) - register one or more buffers
+  *	  for the record.  This function returns a copy of the page
+  *	  image where modifications can be performed.  The second argument
+  *	  indicates if the block is new (i.e. a full page image should be taken).
+  * 3) Apply modification of page images obtained in the previous step.
+  * 4) GenericXLogFinish() - finish construction of generic xlog record.
+  *
+  * The xlog record construction can be canceled at any step by calling
+  * GenericXLogAbort().  All changes made to page images copies will be
+  * discarded.
+  *
+  * Please, note the following points when constructing generic xlog records.
+  * - No direct modifications of page images are allowed!  All modifications
+  *	 must be done in the copies returned by GenericXLogRegister().  In other
+  *	 words the code which makes generic xlog records must never call
+  *	 BufferGetPage().
+  * - Registrations of buffers (step 2) and modifications of page images
+  *	 (step 3) can be mixed in any sequence.  The only restriction is that
+  *	 you can only modify page image after registration of corresponding
+  *	 buffer.
+  * - After registration, the buffer also can be unregistered by calling
+  *	 GenericXLogUnregister(buffer).  In this case the changes made in
+  *	 that particular page image copy will be discarded.
+  * - Generic xlog assumes that pages are using standard layout, i.e., all
+  *	 data between pd_lower and pd_upper will be discarded.
+  * - Maximum number of buffers simultaneously registered for a generic xlog
+  *	 record is MAX_GENERIC_XLOG_PAGES.  An error will be thrown if this limit
+  *	 is exceeded.
+  * - Since you modify copies of page images, GenericXLogStart() doesn't
+  *	 start a critical section.  Thus, you can do memory allocation, error
+  *	 throwing etc between GenericXLogStart() and GenericXLogFinish().
+  *	 The actual critical section is present inside GenericXLogFinish().
+  * - GenericXLogFinish() takes care of marking buffers dirty and setting their
+  *	 LSNs.  You don't need to do this explicitly.
+  * - For unlogged relations, everything works the same except there is no
+  *	 WAL record produced.  Thus, you typically don't need to do any explicit
+  *	 checks for unlogged relations.
+  * - If registered buffer isn't new, generic xlog record contains delta
+  *	 between old and new page images.  This delta is produced by per byte
+  *	 comparison.  This current delta mechanism is not effective for data shifts
+  *	 inside the page and may be improved in the future.
+  * - Generic xlog redo function will acquire exclusive locks on buffers
+  *	 in the same order they were registered.  After redo of all changes,
+  *	 the locks will be released in the same order.
+  *
+  *
+  * Internally, delta between pages consists of set of fragments.  Each
+  * fragment represents changes made in given region of page.  A fragment is
+  * described as follows:
+  *
+  * - offset of page region (OffsetNumber)
+  * - length of page region (OffsetNumber)
+  * - data - the data to place into described region ('length' number of bytes)
+  *
+  * Unchanged regions of page are not represented in the delta.  As a result,
+  * the delta can be more compact than full page image.  But if the unchanged region
+  * of the page is less than fragment header (offset and length) the delta
+  * would be bigger than the full page image. For this reason we break into fragments
+  * only if the unchanged region is bigger than MATCH_THRESHOLD.
+  *
+  * The worst case for delta size is when we didn't find any unchanged region
+  * in the page. Then size of delta would be size of page plus size of fragment
+  * header.
+  */
+ #define FRAGMENT_HEADER_SIZE	(2 * sizeof(OffsetNumber))
+ #define MATCH_THRESHOLD			FRAGMENT_HEADER_SIZE
+ #define MAX_DELTA_SIZE			BLCKSZ + FRAGMENT_HEADER_SIZE
+ 	ereport(ERROR,
+ 			(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 			 errmsg("GenericXLogUnregister: registered buffer not found")));
+ }

These error messages do not conform to our style guideline. This
particular seems like an internal error, perhaps it should be reported
with elog()? Not really sure about this. As for most of the others I
saw, they all have the calling function name in the errmsg() which we
don't do.

diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
new file mode 100644
index 13af485..262deb2
*** a/src/backend/replication/logical/decode.c
--- b/src/backend/replication/logical/decode.c
*************** LogicalDecodingProcessRecord(LogicalDeco
*** 143,148 ****
--- 143,149 ----
case RM_BRIN_ID:
case RM_COMMIT_TS_ID:
case RM_REPLORIGIN_ID:
+ 		case RM_GENERIC_ID:
/* just deal with xid, and done */
ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(record),
buf.origptr);

I'm really unsure about this one. Here we're saying that logical
decoding won't deal at all with stuff that was done using generic WAL
records. I think that's fine for index AMs, since logical decoding
doesn't deal with indexes anyway, but if somebody tries to implement
something that's not an index using generic WAL records, it will fail
miserably. Now maybe that's a restriction we're okay with, but if
that's so we should say that explicitely.

I think this patch should be documented in the WAL chapter.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#81Petr Jelinek
petr@2ndquadrant.com
In reply to: Alvaro Herrera (#80)
1 attachment(s)
Re: WIP: Access method extendability

On 29/03/16 18:25, Alvaro Herrera wrote:

+ /*-------------------------------------------------------------------------

+  * API for construction of generic xlog records
+  *
+  * This API allows user to construct generic xlog records which describe
+  * difference between pages in a generic way.  This is useful for
+  * extensions which provide custom access methods because they can't
+  * register their own WAL redo routines.
+  *
+  * Each record must be constructed by following these steps:
+  * 1) GenericXLogStart(relation) - start construction of a generic xlog
+  *	  record for the given relation.
+  * 2) GenericXLogRegister(buffer, isNew) - register one or more buffers
+  *	  for the record.  This function returns a copy of the page
+  *	  image where modifications can be performed.  The second argument
+  *	  indicates if the block is new (i.e. a full page image should be taken).
+  * 3) Apply modification of page images obtained in the previous step.
+  * 4) GenericXLogFinish() - finish construction of generic xlog record.
+  *
+  * The xlog record construction can be canceled at any step by calling
+  * GenericXLogAbort().  All changes made to page images copies will be
+  * discarded.
+  *
+  * Please, note the following points when constructing generic xlog records.
+  * - No direct modifications of page images are allowed!  All modifications
+  *	 must be done in the copies returned by GenericXLogRegister().  In other
+  *	 words the code which makes generic xlog records must never call
+  *	 BufferGetPage().
+  * - Registrations of buffers (step 2) and modifications of page images
+  *	 (step 3) can be mixed in any sequence.  The only restriction is that
+  *	 you can only modify page image after registration of corresponding
+  *	 buffer.
+  * - After registration, the buffer also can be unregistered by calling
+  *	 GenericXLogUnregister(buffer).  In this case the changes made in
+  *	 that particular page image copy will be discarded.
+  * - Generic xlog assumes that pages are using standard layout, i.e., all
+  *	 data between pd_lower and pd_upper will be discarded.
+  * - Maximum number of buffers simultaneously registered for a generic xlog
+  *	 record is MAX_GENERIC_XLOG_PAGES.  An error will be thrown if this limit
+  *	 is exceeded.
+  * - Since you modify copies of page images, GenericXLogStart() doesn't
+  *	 start a critical section.  Thus, you can do memory allocation, error
+  *	 throwing etc between GenericXLogStart() and GenericXLogFinish().
+  *	 The actual critical section is present inside GenericXLogFinish().
+  * - GenericXLogFinish() takes care of marking buffers dirty and setting their
+  *	 LSNs.  You don't need to do this explicitly.
+  * - For unlogged relations, everything works the same except there is no
+  *	 WAL record produced.  Thus, you typically don't need to do any explicit
+  *	 checks for unlogged relations.
+  * - If registered buffer isn't new, generic xlog record contains delta
+  *	 between old and new page images.  This delta is produced by per byte
+  *	 comparison.  This current delta mechanism is not effective for data shifts
+  *	 inside the page and may be improved in the future.
+  * - Generic xlog redo function will acquire exclusive locks on buffers
+  *	 in the same order they were registered.  After redo of all changes,
+  *	 the locks will be released in the same order.
+  *
+  *
+  * Internally, delta between pages consists of set of fragments.  Each
+  * fragment represents changes made in given region of page.  A fragment is
+  * described as follows:
+  *
+  * - offset of page region (OffsetNumber)
+  * - length of page region (OffsetNumber)
+  * - data - the data to place into described region ('length' number of bytes)
+  *
+  * Unchanged regions of page are not represented in the delta.  As a result,
+  * the delta can be more compact than full page image.  But if the unchanged region
+  * of the page is less than fragment header (offset and length) the delta
+  * would be bigger than the full page image. For this reason we break into fragments
+  * only if the unchanged region is bigger than MATCH_THRESHOLD.
+  *
+  * The worst case for delta size is when we didn't find any unchanged region
+  * in the page. Then size of delta would be size of page plus size of fragment
+  * header.
+  */
+ #define FRAGMENT_HEADER_SIZE	(2 * sizeof(OffsetNumber))
+ #define MATCH_THRESHOLD			FRAGMENT_HEADER_SIZE
+ #define MAX_DELTA_SIZE			BLCKSZ + FRAGMENT_HEADER_SIZE

I incorporated your changes and did some additional refinements on top
of them still.

Attached is delta against v12, that should cause less issues when
merging for Teodor.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

generic-xlog-12-delta.patchtext/plain; charset=UTF-8; name=generic-xlog-12-delta.patchDownload
diff --git a/src/backend/access/transam/generic_xlog.c b/src/backend/access/transam/generic_xlog.c
index 7ca03bf..eab40a2 100644
--- a/src/backend/access/transam/generic_xlog.c
+++ b/src/backend/access/transam/generic_xlog.c
@@ -19,78 +19,77 @@
 #include "utils/memutils.h"
 
 /*-------------------------------------------------------------------------
- * API for construction of generic xlog records
+ * API for construction of generic xlThis is useful forog records
  *
- * This API allows user to construct generic xlog records which are
- * describing difference between pages in general way.  Thus it's useful
- * for extension which provides custom access methods because they couldn't
- * register their own WAL redo routines.
+ * This API allows user to construct generic xlog records which describe
+ * difference between pages in a generic way.  This is useful for extensions
+ * which provide custom access methods because they can't register their own
+ * WAL redo routines.
  *
- * Generic xlog record should be constructed in following steps.
- * 1) GenericXLogStart(relation) - start construction of generic xlog
- *	  record for given relation.
+ * Each record must be constructed by following these steps:
+ * 1) GenericXLogStart(relation) - start construction of a generic xlog
+ *	  record for the given relation.
  * 2) GenericXLogRegister(buffer, isNew) - register one or more buffers
- *	  for generic xlog record.  This function return a copy of page image
- *	  where modifications should be performed.  The second argument
- *	  indicates that block is new and full image should be taken.
- * 3) Do modification of page images obtained in previous step.
+ *	  for generic xlog record.  This function returns a copy of the page image
+ *	  where modifications can be performed.  The second argument indicates
+ *	  if block is new (i.e. a full page image should be taken).
+ * 3) Apply modification of page images obtained in the previous step.
  * 4) GenericXLogFinish() - finish construction of generic xlog record.
  *
- * Please, note following points while constructing generic xlog records.
+ * The xlog record construction can be canceled at any step by calling
+ * GenericXLogAbort().  All changes made to page images copies will be
+ * discarded.
+ *
+ * Please note following points when constructing generic xlog records.
  * - No direct modifications of page images are allowed! All modifications
- *	 should be done in copies returned by GenericXLogRegister().  Literally
- *	 code which makes generic xlog records should never call
- *	 BufferGetPage() function.
- * - On any step generic xlog record construction could be canceled by
- *	 calling GenericXLogAbort().  All changes made in page images copies
- *	 would be discarded.
+ *	 must be done in copies returned by GenericXLogRegister().  In other words
+ *	 code which makes generic xlog records must never call BufferGetPage().
  * - Registrations of buffers (step 2) and modifications of page images
- *	 (step 3) could be mixed in any sequence.  The only restriction is that
- *	 you can modify page image only after registration of corresponding
+ *	 (step 3) can be mixed in any sequence.  The only restriction is that
+ *	 you can only modify page image after registration of corresponding
  *	 buffer.
- * - After registration buffer also can be unregistered by calling
- *	 GenericXLogUnregister(buffer).  In this case changes made in particular
- *	 page image copy will be discarded.
+ * - After registration, the buffer can also be unregistered by calling
+ *	 GenericXLogUnregister(buffer).  In this case the changes made in
+ *	 that particular page image copy will be discarded.
  * - Generic xlog assumes that pages are using standard layout.  I.e. all
  *	 information between pd_lower and pd_upper will be discarded.
- * - Maximum number of buffers simultaneously registered for generic xlog
- *	 is MAX_GENERIC_XLOG_PAGES.  Error would be thrown if this limit
+ * - Maximum number of buffers simultaneously registered for a generic xlog
+ *	 is MAX_GENERIC_XLOG_PAGES.  Error will be thrown if this limit is
  *	 exceeded.
  * - Since you modify copies of page images, GenericXLogStart() doesn't
  *	 start a critical section.  Thus, you can do memory allocation, error
  *	 throwing etc between GenericXLogStart() and GenericXLogFinish().
- *	 Actual critical section present inside GenericXLogFinish().
- * - GenericXLogFinish() takes care about marking buffers dirty and setting
+ *	 The actual critical section is present inside GenericXLogFinish().
+ * - GenericXLogFinish() takes care of marking buffers dirty and setting
  *	 their LSNs.  You don't need to do this explicitly.
- * - For unlogged relations, everything work the same expect there is no
+ * - For unlogged relations, everything works the same except there is no
  *	 WAL record produced.  Thus, you typically don't need to do any explicit
  *	 checks for unlogged relations.
  * - If registered buffer isn't new, generic xlog record contains delta
- *	 between old and new page images.  This delta is produced by per byte
- *	 comparison.  Current delta mechanist is not effective for data shift
- *	 inside the page.  However, it could be improved in further versions.
+ *	 between old and new page images.  This delta is produced using per byte
+ *	 comparison.  The current delta mechanist is not effective for data shifts
+ *	 inside the page and may be improved in the future.
  * - Generic xlog redo function will acquire exclusive locks to buffers
- *	 in the same order they were registered.  After redo of all changes
- *	 locks would be released in the same order.  That could makes sense for
- *	 concurrency.
+ *	 in the same order as they were registered.  After redo of all changes,
+ *	 locks will be released in the same order.
  *
- * Internally delta between pages consists of set of fragments.  Each fragment
- * represents changes made in given region of page.  Fragment is described
- * as following.
+ * Internally, delta between pages consists of set of fragments.  Each
+ * fragment represents changes made in a given region of a page.  A fragment
+ * is described as following.
  *
  * - offset of page region (OffsetNumber)
  * - length of page region (OffsetNumber)
  * - data - the data to place into described region ('length' number of bytes)
  *
- * Unchanged regions of page are uncovered by these fragments.  This is why
- * delta could be more compact than full page image.  But if unchanged region
- * of page is less than fragment header (offset and length) then it would
- * increase size of delta instead of decreasing.  Thus, we break fragment only
- * for unchanged regions greater than MATCH_THRESHOLD.
+ * Unchanged regions of page are not represented in the delta.  As a result
+ * delta can be more compact than the full page image.  But if the unchanged
+ * region of the page is smaller than the fragment header (offset and length)
+ * the delta would be bigger than the full page image. For this reason we
+ * break fragment only if the unchanged region is bigger than MATCH_THRESHOLD.
  *
  * The worst case for delta size is when we didn't find any unchanged region
- * in the page. Then size of delta would be size of page plus size of fragment
- * header.
+ * in the page. The size of delta will be size of page plus size of fragment
+ * header in that case.
  */
 #define FRAGMENT_HEADER_SIZE	(2 * sizeof(OffsetNumber))
 #define MATCH_THRESHOLD			FRAGMENT_HEADER_SIZE
@@ -168,8 +167,8 @@ writeDelta(PageData *pageData)
 		bool	match;
 
 		/*
-		 * Check if bytes in old and new page images matches.  We don't rely
-		 * data in unallocated area between pd_lower and pd_upper.  Thus we
+		 * Check if bytes in old and new page images matches.  We don't care
+		 * about data in unallocated area between pd_lower and pd_upper.  We
 		 * assume unallocated area to expand with unmatched bytes.  Bytes
 		 * inside unallocated area are assumed to always match.
 		 */
#82Teodor Sigaev
teodor@sigaev.ru
In reply to: Petr Jelinek (#81)
Re: WIP: Access method extendability

I incorporated your changes and did some additional refinements on top of them
still.

Attached is delta against v12, that should cause less issues when merging for
Teodor.

But last version is 13th?

BTW, it would be cool to add odcs in VII. Internals chapter, description should
be similar to index's interface description, i.e. how to use generic WAL interface.

--
Teodor Sigaev E-mail: teodor@sigaev.ru
WWW: http://www.sigaev.ru/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#83Petr Jelinek
petr@2ndquadrant.com
In reply to: Teodor Sigaev (#82)
Re: WIP: Access method extendability

On 29/03/16 19:29, Teodor Sigaev wrote:

I incorporated your changes and did some additional refinements on top
of them
still.

Attached is delta against v12, that should cause less issues when
merging for
Teodor.

But last version is 13th?

No, 12 is last version from Alexander afaics, I named my merge 13, but
somehow the diff was broken so now I just sent diff against Alexander's
work with mine + Alvaro's changes, sorry for confusion.

--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#84Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Teodor Sigaev (#82)
Re: WIP: Access method extendability

On Tue, Mar 29, 2016 at 8:29 PM, Teodor Sigaev <teodor@sigaev.ru> wrote:

I incorporated your changes and did some additional refinements on top of

them
still.

Attached is delta against v12, that should cause less issues when merging
for
Teodor.

But last version is 13th?

BTW, it would be cool to add odcs in VII. Internals chapter, description
should be similar to index's interface description, i.e. how to use generic
WAL interface.

We already have generic WAL interface description in comments to
generic_xlog.c. I'm not fan of duplicating things. What about moving
interface description from comments to docs completely?

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#85Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Alexander Korotkov (#84)
1 attachment(s)
Re: WIP: Access method extendability

On Tue, Mar 29, 2016 at 8:34 PM, Alexander Korotkov <
a.korotkov@postgrespro.ru> wrote:

On Tue, Mar 29, 2016 at 8:29 PM, Teodor Sigaev <teodor@sigaev.ru> wrote:

I incorporated your changes and did some additional refinements on top of

them
still.

Attached is delta against v12, that should cause less issues when
merging for
Teodor.

But last version is 13th?

BTW, it would be cool to add odcs in VII. Internals chapter, description
should be similar to index's interface description, i.e. how to use generic
WAL interface.

We already have generic WAL interface description in comments to
generic_xlog.c. I'm not fan of duplicating things. What about moving
interface description from comments to docs completely?

I heard no objections. There is revision of patch where generic WAL
interface description was moved to documentation. This description
contains improvements by Petr Jelinek, Alvaro Herrera and Markus Nullmeier
(who sent it to me privately).

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

Attachments:

0002-generic-xlog.14.patchapplication/octet-stream; name=0002-generic-xlog.14.patchDownload
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
new file mode 100644
index 30adece..9046f50
*** a/doc/src/sgml/filelist.sgml
--- b/doc/src/sgml/filelist.sgml
***************
*** 100,105 ****
--- 100,106 ----
  <!ENTITY sources    SYSTEM "sources.sgml">
  <!ENTITY storage    SYSTEM "storage.sgml">
  <!ENTITY tablesample-method SYSTEM "tablesample-method.sgml">
+ <!ENTITY generic-wal SYSTEM "generic-wal.sgml">
  
  <!-- contrib information -->
  <!ENTITY contrib         SYSTEM "contrib.sgml">
diff --git a/doc/src/sgml/generic-wal.sgml b/doc/src/sgml/generic-wal.sgml
new file mode 100644
index ...a00c03c
*** a/doc/src/sgml/generic-wal.sgml
--- b/doc/src/sgml/generic-wal.sgml
***************
*** 0 ****
--- 1,139 ----
+ <!-- doc/src/sgml/generic-wal.sgml -->
+ 
+ <chapter id="generic-wal">
+  <title>Generic WAL records</title>
+ 
+   <para>
+    Despite all built in access methods and WAL-logged modules have their own
+    types of WAL records, there is also generic WAL record type which describes
+    changes to pages in a generic way.  This is useful for extensions that
+    provide custom access methods, because they cannot register their own
+    WAL redo routines.
+   </para>
+ 
+   <para>
+    API for contructing generic WAL records is defined in
+    <filename>generic_xlog.h</> and implemented in <filename>generic_xlog.c</>.
+    Each generic WAL record must be constructed by following these steps:
+ 
+    <orderedlist>
+     <listitem>
+      <para>
+       <function>GenericXLogStart(relation)</> - start construction of a generic xlog
+       record for the given relation.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       <function>GenericXLogRegister(buffer, isNew)</> - register one or more buffers
+       (one at a time) for the current generic xlog record.  This function
+       returns a copy of the page image, where modifications can be made.
+       The second argument indicates if the page is new (eventually, this
+       will result in a full page image being put into the xlog record).
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       Apply modifications to page images obtained in the previous step.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       <function>GenericXLogFinish()</> - finish construction of a generic xlog record.
+      </para>
+     </listitem>
+    </orderedlist>
+   </para>
+ 
+   <para>
+    The xlog record construction can be canceled between any of the above
+    steps by calling <function>GenericXLogAbort()</>.  This will discard all
+    changes to the page image copies.
+   </para>
+ 
+   <para>
+    Please note the following points when constructing generic xlog records:
+    <itemizedlist>
+     <listitem>
+      <para>
+       No direct modifications of page images are allowed!  All modifications
+       must be done in copies acquired from <function>GenericXLogRegister()</>.
+       In other words, code which makes generic xlog records must never call
+       <function>BufferGetPage()</>.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       Registrations of buffers (step 2) and modifications of page images
+       (step 3) can be mixed freely, i.e., both steps may be repeated in any
+       sequence.  The only restriction is that you can modify a page image
+       only after the registration of the corresponding buffer.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       After registration, the buffer can also be unregistered by calling
+       <function>GenericXLogUnregister(buffer)</>.  In this case, the changes
+       made to that particular page image copy will be discarded.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       Generic xlog assumes that pages are using standard layout.  I.e., all
+       information between pd_lower and pd_upper will be discarded.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       The maximum number of buffers that can be simultaneously registered
+       for a generic xlog is <literal>MAX_GENERIC_XLOG_PAGES</>.  An error will
+       be thrown if this limit is exceeded.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       Since you modify copies of page images, <function>GenericXLogStart()</>
+       does not start a critical section.  Thus, you can do memory allocation,
+       error throwing, etc. between <function>GenericXLogStart()</> and
+       <function>GenericXLogFinish()</>.  The actual critical section is present
+       inside <function>GenericXLogFinish()</>.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <function>GenericXLogFinish()</> takes care of marking buffers as dirty
+       and setting their LSNs.  You do not need to do this explicitly.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       For unlogged relations, everything works the same except there is no
+       WAL record produced.  Thus, you typically do not need to do any explicit
+       checks for unlogged relations.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       If a registered buffer is not new, the generic xlog record contains
+       a delta between the old and the new page images.  This delta is produced
+       using per byte comparison.  The current delta mechanism is not effective
+       for moving data within a page and may be improved in the future.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       The generic xlog redo function will acquire exclusive locks to buffers
+       in the same order as they were registered.  After redoing all changes,
+       the locks will be released in the same order.
+      </para>
+     </listitem>
+    </itemizedlist>
+   </para>
+ </chapter>
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
new file mode 100644
index 7e82cdc..0346d36
*** a/doc/src/sgml/postgres.sgml
--- b/doc/src/sgml/postgres.sgml
***************
*** 247,252 ****
--- 247,253 ----
    &custom-scan;
    &geqo;
    &indexam;
+   &generic-wal;
    &gist;
    &spgist;
    &gin;
diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
new file mode 100644
index c72a1f2..c0e38fd
*** a/src/backend/access/rmgrdesc/Makefile
--- b/src/backend/access/rmgrdesc/Makefile
*************** subdir = src/backend/access/rmgrdesc
*** 8,16 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o gindesc.o gistdesc.o \
! 	   hashdesc.o heapdesc.o mxactdesc.o nbtdesc.o relmapdesc.o \
! 	   replorigindesc.o seqdesc.o smgrdesc.o spgdesc.o \
  	   standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
  
  include $(top_srcdir)/src/backend/common.mk
--- 8,16 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o genericdesc.o \
! 	   gindesc.o gistdesc.o hashdesc.o heapdesc.o mxactdesc.o nbtdesc.o \
! 	   relmapdesc.o replorigindesc.o seqdesc.o smgrdesc.o spgdesc.o \
  	   standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
  
  include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/genericdesc.c b/src/backend/access/rmgrdesc/genericdesc.c
new file mode 100644
index ...caa9a03
*** a/src/backend/access/rmgrdesc/genericdesc.c
--- b/src/backend/access/rmgrdesc/genericdesc.c
***************
*** 0 ****
--- 1,58 ----
+ /*-------------------------------------------------------------------------
+  *
+  * genericdesc.c
+  *	  rmgr descriptor routines for access/transam/generic_xlog.c
+  *
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/backend/access/rmgrdesc/genericdesc.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/generic_xlog.h"
+ #include "lib/stringinfo.h"
+ #include "storage/relfilenode.h"
+ 
+ /*
+  * Description of generic xlog record: write page regions that this record
+  * overrides.
+  */
+ void
+ generic_desc(StringInfo buf, XLogReaderState *record)
+ {
+ 	Pointer		ptr = XLogRecGetData(record),
+ 				end = ptr + XLogRecGetDataLen(record);
+ 
+ 	while (ptr < end)
+ 	{
+ 		OffsetNumber	offset,
+ 						length;
+ 
+ 		memcpy(&offset, ptr, sizeof(offset));
+ 		ptr += sizeof(offset);
+ 		memcpy(&length, ptr, sizeof(length));
+ 		ptr += sizeof(length);
+ 		ptr += length;
+ 
+ 		if (ptr < end)
+ 			appendStringInfo(buf, "offset %u, length %u; ", offset, length);
+ 		else
+ 			appendStringInfo(buf, "offset %u, length %u", offset, length);
+ 	}
+ 
+ 	return;
+ }
+ 
+ /*
+  * Identification of generic xlog record: we don't distinguish any subtypes
+  * inside generic xlog records.
+  */
+ const char *
+ generic_identify(uint8 info)
+ {
+ 	return "Generic";
+ }
diff --git a/src/backend/access/transam/Makefile b/src/backend/access/transam/Makefile
new file mode 100644
index 94455b2..16fbe47
*** a/src/backend/access/transam/Makefile
--- b/src/backend/access/transam/Makefile
*************** subdir = src/backend/access/transam
*** 12,19 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = clog.o commit_ts.o multixact.o parallel.o rmgr.o slru.o subtrans.o \
! 	timeline.o transam.o twophase.o twophase_rmgr.o varsup.o \
  	xact.o xlog.o xlogarchive.o xlogfuncs.o \
  	xloginsert.o xlogreader.o xlogutils.o
  
--- 12,19 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = clog.o commit_ts.o generic_xlog.o multixact.o parallel.o rmgr.o slru.o \
! 	subtrans.o timeline.o transam.o twophase.o twophase_rmgr.o varsup.o \
  	xact.o xlog.o xlogarchive.o xlogfuncs.o \
  	xloginsert.o xlogreader.o xlogutils.o
  
diff --git a/src/backend/access/transam/generic_xlog.c b/src/backend/access/transam/generic_xlog.c
new file mode 100644
index ...32c2648
*** a/src/backend/access/transam/generic_xlog.c
--- b/src/backend/access/transam/generic_xlog.c
***************
*** 0 ****
--- 1,457 ----
+ /*-------------------------------------------------------------------------
+  *
+  * generic_xlog.c
+  *	 Implementation of generic xlog records.
+  *
+  *
+  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/backend/access/transam/generic_xlog.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/generic_xlog.h"
+ #include "access/xlogutils.h"
+ #include "miscadmin.h"
+ #include "utils/memutils.h"
+ 
+ /*-------------------------------------------------------------------------
+  * Internally, a delta between pages consists of set of fragments.  Each
+  * fragment represents changes made in a given region of a page.  A fragment
+  * is made up as follows:
+  *
+  * - offset of page region (OffsetNumber)
+  * - length of page region (OffsetNumber)
+  * - data - the data to place into the region ('length' number of bytes)
+  *
+  * Unchanged regions of a page are not represented in its delta.  As a
+  * result, a delta can be more compact than the full page image.  But having
+  * an unchanged region in the middle to two fragments that is smaller than
+  * the fragment header (offset and length) does not pay off in terms of the
+  * overall size of the delta. For this reason, we break fragments only if
+  * the unchanged region is bigger than MATCH_THRESHOLD.
+  *
+  * The worst case for delta sizes occurs when we did not find any unchanged
+  * region in the page.  The size of the delta will be the size of the page plus
+  * the size of the fragment header in that case.
+  *-------------------------------------------------------------------------
+  */
+ #define FRAGMENT_HEADER_SIZE	(2 * sizeof(OffsetNumber))
+ #define MATCH_THRESHOLD			FRAGMENT_HEADER_SIZE
+ #define MAX_DELTA_SIZE			BLCKSZ + FRAGMENT_HEADER_SIZE
+ 
+ /* Struct of generic xlog data for single page */
+ typedef struct
+ {
+ 	Buffer	buffer;			/* registered buffer */
+ 	char	image[BLCKSZ];	/* copy of page image for modification */
+ 	char	data[MAX_DELTA_SIZE]; /* delta between page images */
+ 	int		dataLen;		/* space consumed in data field */
+ 	bool	fullImage;		/* are we taking a full image of this page? */
+ } PageData;
+ 
+ /* Enum of generic xlog (gxlog) status */
+ enum GenericXlogStatus
+ {
+ 	GXLOG_NOT_STARTED,	/* gxlog is not started */
+ 	GXLOG_LOGGED,		/* gxlog is started for logged relation */
+ 	GXLOG_UNLOGGED		/* gxlog is started for unlogged relation */
+ };
+ 
+ static enum GenericXlogStatus	genericXlogStatus = GXLOG_NOT_STARTED;
+ static PageData					pages[MAX_GENERIC_XLOG_PAGES];
+ 
+ 
+ static void writeFragment(PageData *pageData, OffsetNumber offset,
+ 						  OffsetNumber len, Pointer data);
+ static void writeDelta(PageData *pageData);
+ static void applyPageRedo(Page page, Pointer data, Size dataSize);
+ 
+ /*
+  * Write next fragment into delta.
+  */
+ static void
+ writeFragment(PageData *pageData, OffsetNumber offset, OffsetNumber length,
+ 			  Pointer data)
+ {
+ 	Pointer			ptr = pageData->data + pageData->dataLen;
+ 
+ 	/* Check if we have enough space */
+ 	Assert(pageData->dataLen + sizeof(offset) +
+ 		   sizeof(length) + length <= sizeof(pageData->data));
+ 
+ 	/* Write fragment data */
+ 	memcpy(ptr, &offset, sizeof(offset));
+ 	ptr += sizeof(offset);
+ 	memcpy(ptr, &length, sizeof(length));
+ 	ptr += sizeof(length);
+ 	memcpy(ptr, data, length);
+ 	ptr += length;
+ 
+ 	pageData->dataLen = ptr - pageData->data;
+ }
+ 
+ /*
+  * Make delta for given page.
+  */
+ static void
+ writeDelta(PageData *pageData)
+ {
+ 	Page			page = BufferGetPage(pageData->buffer),
+ 					image = (Page) pageData->image;
+ 	int				i,
+ 					fragmentBegin = -1,
+ 					fragmentEnd = -1;
+ 	uint16			pageLower = ((PageHeader) page)->pd_lower,
+ 					pageUpper = ((PageHeader) page)->pd_upper,
+ 					imageLower = ((PageHeader) image)->pd_lower,
+ 					imageUpper = ((PageHeader) image)->pd_upper;
+ 
+ 	for (i = 0; i < BLCKSZ; i++)
+ 	{
+ 		bool	match;
+ 
+ 		/*
+ 		 * Check if bytes in old and new page images match.  We do not care
+ 		 * about data in the unallocated area between pd_lower and pd_upper.
+ 		 * We assume the unallocated area to expand with unmatched bytes.
+ 		 * Bytes inside the unallocated area are assumed to always match.
+ 		 */
+ 		if (i < pageLower)
+ 		{
+ 			if (i < imageLower)
+ 				match = (page[i] == image[i]);
+ 			else
+ 				match = false;
+ 		}
+ 		else if (i >= pageUpper)
+ 		{
+ 			if (i >= imageUpper)
+ 				match = (page[i] == image[i]);
+ 			else
+ 				match = false;
+ 		}
+ 		else
+ 		{
+ 			match = true;
+ 		}
+ 
+ 		if (match)
+ 		{
+ 			if (fragmentBegin >= 0)
+ 			{
+ 				/* Matched byte is potentially part of a fragment. */
+ 				if (fragmentEnd < 0)
+ 					fragmentEnd = i;
+ 
+ 				/*
+ 				 * Write next fragment if sequence of matched bytes is longer
+ 				 * than MATCH_THRESHOLD.
+ 				 */
+ 				if (i - fragmentEnd >= MATCH_THRESHOLD)
+ 				{
+ 					writeFragment(pageData, fragmentBegin,
+ 								  fragmentEnd - fragmentBegin,
+ 								  page + fragmentBegin);
+ 					fragmentBegin = -1;
+ 					fragmentEnd = -1;
+ 				}
+ 			}
+ 		}
+ 		else
+ 		{
+ 			/* On unmatched byte, start new fragment if it is not done yet */
+ 			if (fragmentBegin < 0)
+ 				fragmentBegin = i;
+ 			fragmentEnd = -1;
+ 		}
+ 	}
+ 
+ 	if (fragmentBegin >= 0)
+ 		writeFragment(pageData, fragmentBegin,
+ 					  BLCKSZ - fragmentBegin,
+ 					  page + fragmentBegin);
+ 
+ #ifdef WAL_DEBUG
+ 	/*
+ 	 * If xlog debug is enabled, then check produced delta.  Result of delta
+ 	 * application to saved image should be the same as current page state.
+ 	 */
+ 	if (XLOG_DEBUG)
+ 	{
+ 		char	tmp[BLCKSZ];
+ 		memcpy(tmp, image, BLCKSZ);
+ 		applyPageRedo(tmp, pageData->data, pageData->dataLen);
+ 		if (memcmp(tmp, page, pageLower)
+ 			|| memcmp(tmp + pageUpper, page + pageUpper, BLCKSZ - pageUpper))
+ 			elog(ERROR, "result of generic xlog apply does not match");
+ 	}
+ #endif
+ }
+ 
+ /*
+  * Start new generic xlog record.
+  */
+ void
+ GenericXLogStart(Relation relation)
+ {
+ 	int i;
+ 
+ 	if (genericXlogStatus != GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogStart: generic xlog is already started")));
+ 
+ 	genericXlogStatus = RelationNeedsWAL(relation) ? GXLOG_LOGGED : GXLOG_UNLOGGED;
+ 
+ 	for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 	{
+ 		pages[i].buffer = InvalidBuffer;
+ 	}
+ }
+ 
+ /*
+  * Register new buffer for generic xlog record.
+  */
+ Page
+ GenericXLogRegister(Buffer buffer, bool isNew)
+ {
+ 	int block_id;
+ 
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogRegister: generic xlog is not started")));
+ 
+ 	/* Place new buffer to unused slot in array */
+ 	for (block_id = 0; block_id < MAX_GENERIC_XLOG_PAGES; block_id++)
+ 	{
+ 		if (BufferIsInvalid(pages[block_id].buffer))
+ 		{
+ 			pages[block_id].buffer = buffer;
+ 			memcpy(pages[block_id].image, BufferGetPage(buffer), BLCKSZ);
+ 			pages[block_id].dataLen = 0;
+ 			pages[block_id].fullImage = isNew;
+ 			return (Page)pages[block_id].image;
+ 		}
+ 		else if (pages[block_id].buffer == buffer)
+ 		{
+ 			/*
+ 			 * Buffer is already registered.  Just return the image, which is
+ 			 * already prepared.
+ 			 */
+ 			return (Page)pages[block_id].image;
+ 		}
+ 	}
+ 
+ 	ereport(ERROR,
+ 			(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ 			 errmsg("GenericXLogRegister: maximum number of %d buffers is exceeded",
+ 					MAX_GENERIC_XLOG_PAGES)));
+ 
+ 	/* keep compiler quiet */
+ 	return NULL;
+ }
+ 
+ /*
+  * Unregister particular buffer for generic xlog record.
+  */
+ void
+ GenericXLogUnregister(Buffer buffer)
+ {
+ 	int block_id;
+ 
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogUnregister: generic xlog is not started")));
+ 
+ 	/* Find block in array to unregister */
+ 	for (block_id = 0; block_id < MAX_GENERIC_XLOG_PAGES; block_id++)
+ 	{
+ 		if (pages[block_id].buffer == buffer)
+ 		{
+ 			/*
+ 			 * Preserve order of pages in array because it could matter for
+ 			 * concurrency.
+ 			 */
+ 			memmove(&pages[block_id], &pages[block_id + 1],
+ 					(MAX_GENERIC_XLOG_PAGES - block_id - 1) * sizeof(PageData));
+ 			pages[MAX_GENERIC_XLOG_PAGES - 1].buffer = InvalidBuffer;
+ 			return;
+ 		}
+ 	}
+ 
+ 	ereport(ERROR,
+ 			(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 			 errmsg("GenericXLogUnregister: registered buffer not found")));
+ }
+ 
+ /*
+  * Put all changes in registered buffers to generic xlog record.
+  */
+ XLogRecPtr
+ GenericXLogFinish(void)
+ {
+ 	XLogRecPtr lsn = InvalidXLogRecPtr;
+ 	int i;
+ 
+ 	if (genericXlogStatus == GXLOG_LOGGED)
+ 	{
+ 		/* Logged relation: make xlog record in critical section. */
+ 		START_CRIT_SECTION();
+ 		XLogBeginInsert();
+ 
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			char	tmp[BLCKSZ];
+ 
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 
+ 			/* Swap current and saved page image. */
+ 			memcpy(tmp, pages[i].image, BLCKSZ);
+ 			memcpy(pages[i].image, BufferGetPage(pages[i].buffer), BLCKSZ);
+ 			memcpy(BufferGetPage(pages[i].buffer), tmp, BLCKSZ);
+ 
+ 			if (pages[i].fullImage)
+ 			{
+ 				/* A full page image does not require anything special */
+ 				XLogRegisterBuffer(i, pages[i].buffer, REGBUF_FORCE_IMAGE);
+ 			}
+ 			else
+ 			{
+ 				/*
+ 				 * In normal mode, calculate delta and write it as data
+ 				 * associated with this page.
+ 				 */
+ 				XLogRegisterBuffer(i, pages[i].buffer, REGBUF_STANDARD);
+ 				writeDelta(&pages[i]);
+ 				XLogRegisterBufData(i, pages[i].data, pages[i].dataLen);
+ 			}
+ 		}
+ 
+ 		/* Insert xlog record */
+ 		lsn = XLogInsert(RM_GENERIC_ID, 0);
+ 
+ 		/* Set LSN and mark buffers dirty */
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 			PageSetLSN(BufferGetPage(pages[i].buffer), lsn);
+ 			MarkBufferDirty(pages[i].buffer);
+ 		}
+ 		END_CRIT_SECTION();
+ 	}
+ 	else if (genericXlogStatus == GXLOG_UNLOGGED)
+ 	{
+ 		/* Unlogged relation: skip xlog-related stuff */
+ 		START_CRIT_SECTION();
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			if (BufferIsInvalid(pages[i].buffer))
+ 				continue;
+ 			memcpy(BufferGetPage(pages[i].buffer), pages[i].image, BLCKSZ);
+ 			MarkBufferDirty(pages[i].buffer);
+ 		}
+ 		END_CRIT_SECTION();
+ 	}
+ 	else
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogFinish: generic xlog is not started")));
+ 	}
+ 
+ 	genericXlogStatus = GXLOG_NOT_STARTED;
+ 
+ 	return lsn;
+ }
+ 
+ /*
+  * Abort generic xlog record.
+  */
+ void
+ GenericXLogAbort(void)
+ {
+ 	if (genericXlogStatus == GXLOG_NOT_STARTED)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("GenericXLogAbort: generic xlog is not started")));
+ 
+ 	genericXlogStatus = GXLOG_NOT_STARTED;
+ }
+ 
+ /*
+  * Apply delta to given page image.
+  */
+ static void
+ applyPageRedo(Page page, Pointer data, Size dataSize)
+ {
+ 	Pointer ptr = data, end = data + dataSize;
+ 
+ 	while (ptr < end)
+ 	{
+ 		OffsetNumber	offset,
+ 						length;
+ 
+ 		memcpy(&offset, ptr, sizeof(offset));
+ 		ptr += sizeof(offset);
+ 		memcpy(&length, ptr, sizeof(length));
+ 		ptr += sizeof(length);
+ 
+ 		memcpy(page + offset, ptr, length);
+ 
+ 		ptr += length;
+ 	}
+ }
+ 
+ /*
+  * Redo function for generic xlog record.
+  */
+ void
+ generic_redo(XLogReaderState *record)
+ {
+ 	uint8		block_id;
+ 	Buffer		buffers[MAX_GENERIC_XLOG_PAGES] = {InvalidBuffer};
+ 	XLogRecPtr	lsn = record->EndRecPtr;
+ 
+ 	Assert(record->max_block_id < MAX_GENERIC_XLOG_PAGES);
+ 
+ 	/* Interate over blocks */
+ 	for (block_id = 0; block_id <= record->max_block_id; block_id++)
+ 	{
+ 		XLogRedoAction action;
+ 
+ 		if (!XLogRecHasBlockRef(record, block_id))
+ 			continue;
+ 
+ 		action = XLogReadBufferForRedo(record, block_id, &buffers[block_id]);
+ 
+ 		/* Apply redo to given block if needed */
+ 		if (action == BLK_NEEDS_REDO)
+ 		{
+ 			Pointer	blockData;
+ 			Size	blockDataSize;
+ 			Page	page;
+ 
+ 			page = BufferGetPage(buffers[block_id]);
+ 			blockData = XLogRecGetBlockData(record, block_id, &blockDataSize);
+ 			applyPageRedo(page, blockData, blockDataSize);
+ 
+ 			PageSetLSN(page, lsn);
+ 			MarkBufferDirty(buffers[block_id]);
+ 		}
+ 	}
+ 
+ 	/* Changes are done: unlock and release all buffers */
+ 	for (block_id = 0; block_id <= record->max_block_id; block_id++)
+ 	{
+ 		if (BufferIsValid(buffers[block_id]))
+ 			UnlockReleaseBuffer(buffers[block_id]);
+ 	}
+ }
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
new file mode 100644
index 7c4d773..7b38c16
*** a/src/backend/access/transam/rmgr.c
--- b/src/backend/access/transam/rmgr.c
***************
*** 11,16 ****
--- 11,17 ----
  #include "access/commit_ts.h"
  #include "access/gin.h"
  #include "access/gist_private.h"
+ #include "access/generic_xlog.h"
  #include "access/hash.h"
  #include "access/heapam_xlog.h"
  #include "access/brin_xlog.h"
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
new file mode 100644
index 13af485..262deb2
*** a/src/backend/replication/logical/decode.c
--- b/src/backend/replication/logical/decode.c
*************** LogicalDecodingProcessRecord(LogicalDeco
*** 143,148 ****
--- 143,149 ----
  		case RM_BRIN_ID:
  		case RM_COMMIT_TS_ID:
  		case RM_REPLORIGIN_ID:
+ 		case RM_GENERIC_ID:
  			/* just deal with xid, and done */
  			ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(record),
  									buf.origptr);
diff --git a/src/bin/pg_xlogdump/.gitignore b/src/bin/pg_xlogdump/.gitignore
new file mode 100644
index eebaf30..33a1acf
*** a/src/bin/pg_xlogdump/.gitignore
--- b/src/bin/pg_xlogdump/.gitignore
***************
*** 4,9 ****
--- 4,10 ----
  /clogdesc.c
  /committsdesc.c
  /dbasedesc.c
+ /genericdesc.c
  /gindesc.c
  /gistdesc.c
  /hashdesc.c
diff --git a/src/bin/pg_xlogdump/rmgrdesc.c b/src/bin/pg_xlogdump/rmgrdesc.c
new file mode 100644
index f9cd395..cff7e59
*** a/src/bin/pg_xlogdump/rmgrdesc.c
--- b/src/bin/pg_xlogdump/rmgrdesc.c
***************
*** 11,16 ****
--- 11,17 ----
  #include "access/brin_xlog.h"
  #include "access/clog.h"
  #include "access/commit_ts.h"
+ #include "access/generic_xlog.h"
  #include "access/gin.h"
  #include "access/gist_private.h"
  #include "access/hash.h"
diff --git a/src/include/access/generic_xlog.h b/src/include/access/generic_xlog.h
new file mode 100644
index ...49249e0
*** a/src/include/access/generic_xlog.h
--- b/src/include/access/generic_xlog.h
***************
*** 0 ****
--- 1,36 ----
+ /*-------------------------------------------------------------------------
+  *
+  * generic_xlog.h
+  *	  Generic xlog API definition.
+  *
+  *
+  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/access/generic_xlog.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef GENERIC_XLOG_H
+ #define GENERIC_XLOG_H
+ 
+ #include "access/xlog.h"
+ #include "access/xlog_internal.h"
+ #include "storage/bufpage.h"
+ #include "utils/rel.h"
+ 
+ #define MAX_GENERIC_XLOG_PAGES	  3
+ 
+ /* API for construction of generic xlog records */
+ extern void GenericXLogStart(Relation relation);
+ extern Page GenericXLogRegister(Buffer buffer, bool isNew);
+ extern void GenericXLogUnregister(Buffer buffer);
+ extern XLogRecPtr GenericXLogFinish(void);
+ extern void GenericXLogAbort(void);
+ 
+ /* functions defined for rmgr */
+ extern void generic_redo(XLogReaderState *record);
+ extern const char *generic_identify(uint8 info);
+ extern void generic_desc(StringInfo buf, XLogReaderState *record);
+ 
+ #endif   /* GENERIC_XLOG_H */
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
new file mode 100644
index fab912d..3cfe6f7
*** a/src/include/access/rmgrlist.h
--- b/src/include/access/rmgrlist.h
*************** PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo
*** 45,47 ****
--- 45,48 ----
  PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL)
  PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL)
  PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL)
+ PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL)
#86Aleksander Alekseev
a.alekseev@postgrespro.ru
In reply to: Alexander Korotkov (#85)
Re: WIP: Access method extendability

Hello

I did a brief review of bloom contrib and I don't think I like it much.
Here are some issues I believe should be fixed before committing it to
PostgreSQL.

1) Most of the code is not commented. Every procedure should at least
have a breif description of what it does, what arguments it receives
and what it returns. Same for structures and enums.

2) There are only 3 Asserts. For sure there are much more invariants in
this contrib.

3) I don't like this code (blinsert.c):

```
typedef struct
{
BloomState blstate;
MemoryContext tmpCtx;
char data[BLCKSZ];
int64 count;
} BloomBuildState;

/* ... */

/*
* (Re)initialize cached page in BloomBuildState.
*/
static void
initCachedPage(BloomBuildState *buildstate)
{
memset(buildstate->data, 0, BLCKSZ);
BloomInitPage(buildstate->data, 0);
buildstate->count = 0;
}
```

It looks wrong because 1) blkstate and tmpCtx are left uninitialized 2)
as we discussed recently [1]/messages/by-id/56EFF347.20500@anastigmatix.net you should avoid leaving "holes" with
uninitialized data in structures. Please fix this or provide a comment
that describes why things are done here the way they are done.

Perhaps there are also other places like this that I missed.

4) I don't think I like such a code either:

```
/* blutuls.c */

static BloomOptions *
makeDefaultBloomOptions(BloomOptions *opts)
{
int i;

if (!opts)
opts = palloc0(sizeof(BloomOptions));

/* ... */

/* see also blvacuum.c and other places I could miss */
```

Sometimes we create a new zero-initialized structure and sometimes we
use a provided one filled with some data. If I'll use this procedure
multiple times in a loop memory will leak. Thus sometimes we need
pfree() returned value manually and sometimes we don't. Such a code is
hard to reason about. You could do it much simpler choosing only one
thing to do --- either allocate a new structure or use a provided one.

5) Code is not pgindent'ed and has many trailing spaces.

[1]: /messages/by-id/56EFF347.20500@anastigmatix.net

--
Best regards,
Aleksander Alekseev
http://eax.me/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#87Jose Luis Tallon
jltallon@adv-solutions.net
In reply to: Aleksander Alekseev (#86)
Re: WIP: Access method extendability

Referenced by commit commit 473b93287040b20017cc25a157cffdc5b978c254 ("Support CREATE ACCESS METHOD"), commited by alvherre

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#88Teodor Sigaev
teodor@sigaev.ru
In reply to: Alexander Korotkov (#85)
Re: WIP: Access method extendability

GenericXLogStart(Relation relation)
{
...
if (genericXlogStatus != GXLOG_NOT_STARTED)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("GenericXLogStart: generic xlog is already started")));

Hmm, seems, generic wal whiil be in incorrect state if exception occurs between
GenericXLogStart() and GenericXLogFinish() calls because static variable
genericXlogStatus will contain GXLOG_LOGGED/GXLOG_UNLOGGED status.

Suppose, it could be solved by different ways
- remove all static variable, so, GenericXLogStart() will return an struct
(object) which incapsulated all data needed to generic wal work. As I can
see, in case of exception there isn't ane needing to extra cleanup. Also,
it would allow to use generic wal for two or more relations at the same time,
although I don't know any useful example for such feature.
- add callback via RegisterResourceReleaseCallback() which will cleanup state
of genericXlogStatus variable

--
Teodor Sigaev E-mail: teodor@sigaev.ru
WWW: http://www.sigaev.ru/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#89Teodor Sigaev
teodor@sigaev.ru
In reply to: Aleksander Alekseev (#86)
Re: WIP: Access method extendability

as we discussed recently [1] you should avoid leaving "holes" with
uninitialized data in structures. Please fix this or provide a comment
that describes why things are done here the way they are done.
[1] /messages/by-id/56EFF347.20500@anastigmatix.net

That discussion is about SQL-level types which could be stored on disk, not
about in-memory structs

--
Teodor Sigaev E-mail: teodor@sigaev.ru
WWW: http://www.sigaev.ru/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#90Aleksander Alekseev
a.alekseev@postgrespro.ru
In reply to: Teodor Sigaev (#89)
Re: WIP: Access method extendability

/messages/by-id/56EFF347.20500@anastigmatix.net

That discussion is about SQL-level types which could be stored on
disk, not about in-memory structs

I must respectfully disagree. That discussion is also about memory
sanitizers and using them on buildfarms. Lets say you initialize a
structure like this:

st->f1 = 111;
st->f2 = 222;

... without using memset, so there could be a "hole" with uninitialized
data somewhere in between of f1 and f2.

Than some code calculates a hash of this structure or does memcpy - and
1) You get unreproducible behavior - hash is always different for the
same structure, thus it is stored in different hash buckets, etc, and as
a result you got bugs that sometimes reproduce and sometimes do not
2) There is one more place where sanitizers could report accesses to
uninitialized values and thus they still can't be used on buildfarms
where they could find a lot of serious bugs automatically. I believe
MemorySanitizer is smart enough to recognize trivial memcpy case, but
it could be confused in more complicated cases.

Anyway I suggest continue discussion of whether we should make
PostgreSQL sanitizers-friendly or not in a corresponding thread. So far
no one spoke against this idea. Thus I don't think that new patches
should complicate implementing it. Especially considering that it's
very simple to do and even is considered a good practice according to
PostgreSQL documentation.

--
Best regards,
Aleksander Alekseev
http://eax.me/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#91Markus Nullmeier
dq124@uni-heidelberg.de
In reply to: Teodor Sigaev (#88)
1 attachment(s)
Re: WIP: Access method extendability

Alexander Korotkov <a(dot)korotkov(at)postgrespro(dot)ru> wrote:

I heard no objections. There is revision of patch where generic WAL
interface description was moved to documentation. This description
contains improvements by Petr Jelinek, Alvaro Herrera and Markus Nullmeier

Attached are a few more small fixes as an incremental patch (typos / etc.).

--
Markus Nullmeier http://www.g-vo.org
German Astrophysical Virtual Observatory (GAVO)

Attachments:

generic-xlog-14-fixes-delta.patchtext/x-patch; name=generic-xlog-14-fixes-delta.patchDownload
diff --git a/doc/src/sgml/generic-wal.sgml b/doc/src/sgml/generic-wal.sgml
index a00c03c..d756e33 100644
--- a/doc/src/sgml/generic-wal.sgml
+++ b/doc/src/sgml/generic-wal.sgml
@@ -4,15 +4,15 @@
  <title>Generic WAL records</title>
 
   <para>
-   Despite all built in access methods and WAL-logged modules have their own
-   types of WAL records, there is also generic WAL record type which describes
+   Despite all built-in access methods and WAL-logged modules having their own
+   types of WAL records, there is also a generic WAL record type, which describes
    changes to pages in a generic way.  This is useful for extensions that
    provide custom access methods, because they cannot register their own
    WAL redo routines.
   </para>
 
   <para>
-   API for contructing generic WAL records is defined in
+   The API for contructing generic WAL records is defined in
    <filename>generic_xlog.h</> and implemented in <filename>generic_xlog.c</>.
    Each generic WAL record must be constructed by following these steps:
 
diff --git a/src/backend/access/transam/generic_xlog.c b/src/backend/access/transam/generic_xlog.c
index 32c2648..1a720fa 100644
--- a/src/backend/access/transam/generic_xlog.c
+++ b/src/backend/access/transam/generic_xlog.c
@@ -19,7 +19,7 @@
 #include "utils/memutils.h"
 
 /*-------------------------------------------------------------------------
- * Internally, a delta between pages consists of set of fragments.  Each
+ * Internally, a delta between pages consists of a set of fragments.  Each
  * fragment represents changes made in a given region of a page.  A fragment
  * is made up as follows:
  *
@@ -29,7 +29,7 @@
  *
  * Unchanged regions of a page are not represented in its delta.  As a
  * result, a delta can be more compact than the full page image.  But having
- * an unchanged region in the middle to two fragments that is smaller than
+ * an unchanged region in the middle of two fragments that is smaller than
  * the fragment header (offset and length) does not pay off in terms of the
  * overall size of the delta. For this reason, we break fragments only if
  * the unchanged region is bigger than MATCH_THRESHOLD.
@@ -422,7 +422,7 @@ generic_redo(XLogReaderState *record)
 
 	Assert(record->max_block_id < MAX_GENERIC_XLOG_PAGES);
 
-	/* Interate over blocks */
+	/* Iterate over blocks */
 	for (block_id = 0; block_id <= record->max_block_id; block_id++)
 	{
 		XLogRedoAction action;
#92Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Aleksander Alekseev (#90)
2 attachment(s)
Re: WIP: Access method extendability

Hi!

New revision of patches is attached.

Changes are following:
1) API of generic xlog was changed: now there is no static variables,
GenericXLogStart()
returns palloc'd struct.
2) Generic xlog use elog instead ereport since it reports internal errors
which shouldn't happen normally.
3) Error messages doesn't contains name of the function.
4) Bloom contrib was pgindented.
5) More comments for bloomb.
6) One more assert was added to bloom.
7) makeDefaultBloomOptions was renamed to adjustBloomOptions. Now it only
modifies its parameter. palloc is done outside of this function.
8) BloomBuildState is explicitly zeroed.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

Attachments:

0002-generic-xlog.15.patchapplication/octet-stream; name=0002-generic-xlog.15.patchDownload
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
new file mode 100644
index 30adece..9046f50
*** a/doc/src/sgml/filelist.sgml
--- b/doc/src/sgml/filelist.sgml
***************
*** 100,105 ****
--- 100,106 ----
  <!ENTITY sources    SYSTEM "sources.sgml">
  <!ENTITY storage    SYSTEM "storage.sgml">
  <!ENTITY tablesample-method SYSTEM "tablesample-method.sgml">
+ <!ENTITY generic-wal SYSTEM "generic-wal.sgml">
  
  <!-- contrib information -->
  <!ENTITY contrib         SYSTEM "contrib.sgml">
diff --git a/doc/src/sgml/generic-wal.sgml b/doc/src/sgml/generic-wal.sgml
new file mode 100644
index ...6655f22
*** a/doc/src/sgml/generic-wal.sgml
--- b/doc/src/sgml/generic-wal.sgml
***************
*** 0 ****
--- 1,141 ----
+ <!-- doc/src/sgml/generic-wal.sgml -->
+ 
+ <chapter id="generic-wal">
+  <title>Generic WAL records</title>
+ 
+   <para>
+    Despite all built-in access methods and WAL-logged modules having their own
+    types of WAL records, there is also a generic WAL record type, which describes
+    changes to pages in a generic way.  This is useful for extensions that
+    provide custom access methods, because they cannot register their own
+    WAL redo routines.
+   </para>
+ 
+   <para>
+    The API for contructing generic WAL records is defined in
+    <filename>generic_xlog.h</> and implemented in <filename>generic_xlog.c</>.
+    Each generic WAL record must be constructed by following these steps:
+ 
+    <orderedlist>
+     <listitem>
+      <para>
+       <function>state = GenericXLogStart(relation)</> &mdash; start
+       construction of a generic xlog record for the given relation.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       <function>page = GenericXLogRegister(state, buffer, isNew)</> &mdash;
+       register one or more buffers (one at a time) for the current generic
+       xlog record.  This function returns a copy of the page image, where
+       modifications can be made.  The second argument indicates if the page
+       is new (eventually, this will result in a full page image being put into
+       the xlog record).
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       Apply modifications to page images obtained in the previous step.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       <function>GenericXLogAbort(state)</> &mdash; finish construction of
+       a generic xlog record.
+      </para>
+     </listitem>
+    </orderedlist>
+   </para>
+ 
+   <para>
+    The xlog record construction can be canceled between any of the above
+    steps by calling <function>GenericXLogAbort()</>.  This will discard all
+    changes to the page image copies.
+   </para>
+ 
+   <para>
+    Please note the following points when constructing generic xlog records:
+    <itemizedlist>
+     <listitem>
+      <para>
+       No direct modifications of page images are allowed!  All modifications
+       must be done in copies acquired from <function>GenericXLogRegister()</>.
+       In other words, code which makes generic xlog records must never call
+       <function>BufferGetPage()</>.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       Registrations of buffers (step 2) and modifications of page images
+       (step 3) can be mixed freely, i.e., both steps may be repeated in any
+       sequence.  The only restriction is that you can modify a page image
+       only after the registration of the corresponding buffer.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       After registration, the buffer can also be unregistered by calling
+       <function>GenericXLogUnregister(buffer)</>.  In this case, the changes
+       made to that particular page image copy will be discarded.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       Generic xlog assumes that pages are using standard layout.  I.e., all
+       information between pd_lower and pd_upper will be discarded.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       The maximum number of buffers that can be simultaneously registered
+       for a generic xlog is <literal>MAX_GENERIC_XLOG_PAGES</>.  An error will
+       be thrown if this limit is exceeded.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       Since you modify copies of page images, <function>GenericXLogStart()</>
+       does not start a critical section.  Thus, you can do memory allocation,
+       error throwing, etc. between <function>GenericXLogStart()</> and
+       <function>GenericXLogFinish()</>.  The actual critical section is present
+       inside <function>GenericXLogFinish()</>.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       <function>GenericXLogFinish()</> takes care of marking buffers as dirty
+       and setting their LSNs.  You do not need to do this explicitly.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       For unlogged relations, everything works the same except there is no
+       WAL record produced.  Thus, you typically do not need to do any explicit
+       checks for unlogged relations.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       If a registered buffer is not new, the generic xlog record contains
+       a delta between the old and the new page images.  This delta is produced
+       using per byte comparison.  The current delta mechanism is not effective
+       for moving data within a page and may be improved in the future.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       The generic xlog redo function will acquire exclusive locks to buffers
+       in the same order as they were registered.  After redoing all changes,
+       the locks will be released in the same order.
+      </para>
+     </listitem>
+    </itemizedlist>
+   </para>
+ </chapter>
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
new file mode 100644
index 7e82cdc..0346d36
*** a/doc/src/sgml/postgres.sgml
--- b/doc/src/sgml/postgres.sgml
***************
*** 247,252 ****
--- 247,253 ----
    &custom-scan;
    &geqo;
    &indexam;
+   &generic-wal;
    &gist;
    &spgist;
    &gin;
diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
new file mode 100644
index c72a1f2..c0e38fd
*** a/src/backend/access/rmgrdesc/Makefile
--- b/src/backend/access/rmgrdesc/Makefile
*************** subdir = src/backend/access/rmgrdesc
*** 8,16 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o gindesc.o gistdesc.o \
! 	   hashdesc.o heapdesc.o mxactdesc.o nbtdesc.o relmapdesc.o \
! 	   replorigindesc.o seqdesc.o smgrdesc.o spgdesc.o \
  	   standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
  
  include $(top_srcdir)/src/backend/common.mk
--- 8,16 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = brindesc.o clogdesc.o committsdesc.o dbasedesc.o genericdesc.o \
! 	   gindesc.o gistdesc.o hashdesc.o heapdesc.o mxactdesc.o nbtdesc.o \
! 	   relmapdesc.o replorigindesc.o seqdesc.o smgrdesc.o spgdesc.o \
  	   standbydesc.o tblspcdesc.o xactdesc.o xlogdesc.o
  
  include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/rmgrdesc/genericdesc.c b/src/backend/access/rmgrdesc/genericdesc.c
new file mode 100644
index ...caa9a03
*** a/src/backend/access/rmgrdesc/genericdesc.c
--- b/src/backend/access/rmgrdesc/genericdesc.c
***************
*** 0 ****
--- 1,58 ----
+ /*-------------------------------------------------------------------------
+  *
+  * genericdesc.c
+  *	  rmgr descriptor routines for access/transam/generic_xlog.c
+  *
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/backend/access/rmgrdesc/genericdesc.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/generic_xlog.h"
+ #include "lib/stringinfo.h"
+ #include "storage/relfilenode.h"
+ 
+ /*
+  * Description of generic xlog record: write page regions that this record
+  * overrides.
+  */
+ void
+ generic_desc(StringInfo buf, XLogReaderState *record)
+ {
+ 	Pointer		ptr = XLogRecGetData(record),
+ 				end = ptr + XLogRecGetDataLen(record);
+ 
+ 	while (ptr < end)
+ 	{
+ 		OffsetNumber	offset,
+ 						length;
+ 
+ 		memcpy(&offset, ptr, sizeof(offset));
+ 		ptr += sizeof(offset);
+ 		memcpy(&length, ptr, sizeof(length));
+ 		ptr += sizeof(length);
+ 		ptr += length;
+ 
+ 		if (ptr < end)
+ 			appendStringInfo(buf, "offset %u, length %u; ", offset, length);
+ 		else
+ 			appendStringInfo(buf, "offset %u, length %u", offset, length);
+ 	}
+ 
+ 	return;
+ }
+ 
+ /*
+  * Identification of generic xlog record: we don't distinguish any subtypes
+  * inside generic xlog records.
+  */
+ const char *
+ generic_identify(uint8 info)
+ {
+ 	return "Generic";
+ }
diff --git a/src/backend/access/transam/Makefile b/src/backend/access/transam/Makefile
new file mode 100644
index 94455b2..16fbe47
*** a/src/backend/access/transam/Makefile
--- b/src/backend/access/transam/Makefile
*************** subdir = src/backend/access/transam
*** 12,19 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = clog.o commit_ts.o multixact.o parallel.o rmgr.o slru.o subtrans.o \
! 	timeline.o transam.o twophase.o twophase_rmgr.o varsup.o \
  	xact.o xlog.o xlogarchive.o xlogfuncs.o \
  	xloginsert.o xlogreader.o xlogutils.o
  
--- 12,19 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = clog.o commit_ts.o generic_xlog.o multixact.o parallel.o rmgr.o slru.o \
! 	subtrans.o timeline.o transam.o twophase.o twophase_rmgr.o varsup.o \
  	xact.o xlog.o xlogarchive.o xlogfuncs.o \
  	xloginsert.o xlogreader.o xlogutils.o
  
diff --git a/src/backend/access/transam/generic_xlog.c b/src/backend/access/transam/generic_xlog.c
new file mode 100644
index ...0b5b8fa
*** a/src/backend/access/transam/generic_xlog.c
--- b/src/backend/access/transam/generic_xlog.c
***************
*** 0 ****
--- 1,428 ----
+ /*-------------------------------------------------------------------------
+  *
+  * generic_xlog.c
+  *	 Implementation of generic xlog records.
+  *
+  *
+  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/backend/access/transam/generic_xlog.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/generic_xlog.h"
+ #include "access/xlogutils.h"
+ #include "miscadmin.h"
+ #include "utils/memutils.h"
+ 
+ /*-------------------------------------------------------------------------
+  * Internally, a delta between pages consists of a set of fragments.  Each
+  * fragment represents changes made in a given region of a page.  A fragment
+  * is made up as follows:
+  *
+  * - offset of page region (OffsetNumber)
+  * - length of page region (OffsetNumber)
+  * - data - the data to place into the region ('length' number of bytes)
+  *
+  * Unchanged regions of a page are not represented in its delta.  As a
+  * result, a delta can be more compact than the full page image.  But having
+  * an unchanged region in the middle of two fragments that is smaller than
+  * the fragment header (offset and length) does not pay off in terms of the
+  * overall size of the delta. For this reason, we break fragments only if
+  * the unchanged region is bigger than MATCH_THRESHOLD.
+  *
+  * The worst case for delta sizes occurs when we did not find any unchanged
+  * region in the page.  The size of the delta will be the size of the page plus
+  * the size of the fragment header in that case.
+  *-------------------------------------------------------------------------
+  */
+ #define FRAGMENT_HEADER_SIZE	(2 * sizeof(OffsetNumber))
+ #define MATCH_THRESHOLD			FRAGMENT_HEADER_SIZE
+ #define MAX_DELTA_SIZE			BLCKSZ + FRAGMENT_HEADER_SIZE
+ 
+ /* Struct of generic xlog data for single page */
+ typedef struct
+ {
+ 	Buffer	buffer;			/* registered buffer */
+ 	char	image[BLCKSZ];	/* copy of page image for modification */
+ 	char	data[MAX_DELTA_SIZE]; /* delta between page images */
+ 	int		dataLen;		/* space consumed in data field */
+ 	bool	fullImage;		/* are we taking a full image of this page? */
+ } PageData;
+ 
+ /* State of generic xlog record construction */
+ struct GenericXLogState
+ {
+ 	bool		isLogged;
+ 	PageData	pages[MAX_GENERIC_XLOG_PAGES];
+ };
+ 
+ static void writeFragment(PageData *pageData, OffsetNumber offset,
+ 						  OffsetNumber len, Pointer data);
+ static void writeDelta(PageData *pageData);
+ static void applyPageRedo(Page page, Pointer data, Size dataSize);
+ 
+ /*
+  * Write next fragment into delta.
+  */
+ static void
+ writeFragment(PageData *pageData, OffsetNumber offset, OffsetNumber length,
+ 			  Pointer data)
+ {
+ 	Pointer			ptr = pageData->data + pageData->dataLen;
+ 
+ 	/* Check if we have enough space */
+ 	Assert(pageData->dataLen + sizeof(offset) +
+ 		   sizeof(length) + length <= sizeof(pageData->data));
+ 
+ 	/* Write fragment data */
+ 	memcpy(ptr, &offset, sizeof(offset));
+ 	ptr += sizeof(offset);
+ 	memcpy(ptr, &length, sizeof(length));
+ 	ptr += sizeof(length);
+ 	memcpy(ptr, data, length);
+ 	ptr += length;
+ 
+ 	pageData->dataLen = ptr - pageData->data;
+ }
+ 
+ /*
+  * Make delta for given page.
+  */
+ static void
+ writeDelta(PageData *pageData)
+ {
+ 	Page			page = BufferGetPage(pageData->buffer),
+ 					image = (Page) pageData->image;
+ 	int				i,
+ 					fragmentBegin = -1,
+ 					fragmentEnd = -1;
+ 	uint16			pageLower = ((PageHeader) page)->pd_lower,
+ 					pageUpper = ((PageHeader) page)->pd_upper,
+ 					imageLower = ((PageHeader) image)->pd_lower,
+ 					imageUpper = ((PageHeader) image)->pd_upper;
+ 
+ 	for (i = 0; i < BLCKSZ; i++)
+ 	{
+ 		bool	match;
+ 
+ 		/*
+ 		 * Check if bytes in old and new page images match.  We do not care
+ 		 * about data in the unallocated area between pd_lower and pd_upper.
+ 		 * We assume the unallocated area to expand with unmatched bytes.
+ 		 * Bytes inside the unallocated area are assumed to always match.
+ 		 */
+ 		if (i < pageLower)
+ 		{
+ 			if (i < imageLower)
+ 				match = (page[i] == image[i]);
+ 			else
+ 				match = false;
+ 		}
+ 		else if (i >= pageUpper)
+ 		{
+ 			if (i >= imageUpper)
+ 				match = (page[i] == image[i]);
+ 			else
+ 				match = false;
+ 		}
+ 		else
+ 		{
+ 			match = true;
+ 		}
+ 
+ 		if (match)
+ 		{
+ 			if (fragmentBegin >= 0)
+ 			{
+ 				/* Matched byte is potentially part of a fragment. */
+ 				if (fragmentEnd < 0)
+ 					fragmentEnd = i;
+ 
+ 				/*
+ 				 * Write next fragment if sequence of matched bytes is longer
+ 				 * than MATCH_THRESHOLD.
+ 				 */
+ 				if (i - fragmentEnd >= MATCH_THRESHOLD)
+ 				{
+ 					writeFragment(pageData, fragmentBegin,
+ 								  fragmentEnd - fragmentBegin,
+ 								  page + fragmentBegin);
+ 					fragmentBegin = -1;
+ 					fragmentEnd = -1;
+ 				}
+ 			}
+ 		}
+ 		else
+ 		{
+ 			/* On unmatched byte, start new fragment if it is not done yet */
+ 			if (fragmentBegin < 0)
+ 				fragmentBegin = i;
+ 			fragmentEnd = -1;
+ 		}
+ 	}
+ 
+ 	if (fragmentBegin >= 0)
+ 		writeFragment(pageData, fragmentBegin,
+ 					  BLCKSZ - fragmentBegin,
+ 					  page + fragmentBegin);
+ 
+ #ifdef WAL_DEBUG
+ 	/*
+ 	 * If xlog debug is enabled, then check produced delta.  Result of delta
+ 	 * application to saved image should be the same as current page state.
+ 	 */
+ 	if (XLOG_DEBUG)
+ 	{
+ 		char	tmp[BLCKSZ];
+ 		memcpy(tmp, image, BLCKSZ);
+ 		applyPageRedo(tmp, pageData->data, pageData->dataLen);
+ 		if (memcmp(tmp, page, pageLower)
+ 			|| memcmp(tmp + pageUpper, page + pageUpper, BLCKSZ - pageUpper))
+ 			elog(ERROR, "result of generic xlog apply does not match");
+ 	}
+ #endif
+ }
+ 
+ /*
+  * Start new generic xlog record.
+  */
+ GenericXLogState *
+ GenericXLogStart(Relation relation)
+ {
+ 	int					i;
+ 	GenericXLogState   *state;
+ 
+ 	state = (GenericXLogState *) palloc0(sizeof(GenericXLogState));
+ 
+ 	state->isLogged = RelationNeedsWAL(relation);
+ 	for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		state->pages[i].buffer = InvalidBuffer;
+ 
+ 	return state;
+ }
+ 
+ /*
+  * Register new buffer for generic xlog record.
+  */
+ Page
+ GenericXLogRegister(GenericXLogState *state, Buffer buffer, bool isNew)
+ {
+ 	int block_id;
+ 
+ 	/* Place new buffer to unused slot in array */
+ 	for (block_id = 0; block_id < MAX_GENERIC_XLOG_PAGES; block_id++)
+ 	{
+ 		PageData *page = &state->pages[block_id];
+ 		if (BufferIsInvalid(page->buffer))
+ 		{
+ 			page->buffer = buffer;
+ 			memcpy(page->image, BufferGetPage(buffer), BLCKSZ);
+ 			page->dataLen = 0;
+ 			page->fullImage = isNew;
+ 			return (Page)page->image;
+ 		}
+ 		else if (page->buffer == buffer)
+ 		{
+ 			/*
+ 			 * Buffer is already registered.  Just return the image, which is
+ 			 * already prepared.
+ 			 */
+ 			return (Page)page->image;
+ 		}
+ 	}
+ 
+ 	elog(ERROR, "maximum number of %d generic xlog buffers is exceeded",
+ 		 MAX_GENERIC_XLOG_PAGES);
+ 
+ 	/* keep compiler quiet */
+ 	return NULL;
+ }
+ 
+ /*
+  * Unregister particular buffer for generic xlog record.
+  */
+ void
+ GenericXLogUnregister(GenericXLogState *state, Buffer buffer)
+ {
+ 	int block_id;
+ 
+ 	/* Find block in array to unregister */
+ 	for (block_id = 0; block_id < MAX_GENERIC_XLOG_PAGES; block_id++)
+ 	{
+ 		if (state->pages[block_id].buffer == buffer)
+ 		{
+ 			/*
+ 			 * Preserve order of pages in array because it could matter for
+ 			 * concurrency.
+ 			 */
+ 			memmove(&state->pages[block_id], &state->pages[block_id + 1],
+ 					(MAX_GENERIC_XLOG_PAGES - block_id - 1) * sizeof(PageData));
+ 			state->pages[MAX_GENERIC_XLOG_PAGES - 1].buffer = InvalidBuffer;
+ 			return;
+ 		}
+ 	}
+ 
+ 	elog(ERROR, "registered generic xlog buffer not found");
+ }
+ 
+ /*
+  * Put all changes in registered buffers to generic xlog record.
+  */
+ XLogRecPtr
+ GenericXLogFinish(GenericXLogState *state)
+ {
+ 	XLogRecPtr lsn = InvalidXLogRecPtr;
+ 	int i;
+ 
+ 	if (state->isLogged)
+ 	{
+ 		/* Logged relation: make xlog record in critical section. */
+ 		START_CRIT_SECTION();
+ 		XLogBeginInsert();
+ 
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			char		tmp[BLCKSZ];
+ 			PageData   *page = &state->pages[i];
+ 
+ 			if (BufferIsInvalid(page->buffer))
+ 				continue;
+ 
+ 			/* Swap current and saved page image. */
+ 			memcpy(tmp, page->image, BLCKSZ);
+ 			memcpy(page->image, BufferGetPage(page->buffer), BLCKSZ);
+ 			memcpy(BufferGetPage(page->buffer), tmp, BLCKSZ);
+ 
+ 			if (page->fullImage)
+ 			{
+ 				/* A full page image does not require anything special */
+ 				XLogRegisterBuffer(i, page->buffer, REGBUF_FORCE_IMAGE);
+ 			}
+ 			else
+ 			{
+ 				/*
+ 				 * In normal mode, calculate delta and write it as data
+ 				 * associated with this page.
+ 				 */
+ 				XLogRegisterBuffer(i, page->buffer, REGBUF_STANDARD);
+ 				writeDelta(page);
+ 				XLogRegisterBufData(i, page->data, page->dataLen);
+ 			}
+ 		}
+ 
+ 		/* Insert xlog record */
+ 		lsn = XLogInsert(RM_GENERIC_ID, 0);
+ 
+ 		/* Set LSN and mark buffers dirty */
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			PageData   *page = &state->pages[i];
+ 
+ 			if (BufferIsInvalid(page->buffer))
+ 				continue;
+ 			PageSetLSN(BufferGetPage(page->buffer), lsn);
+ 			MarkBufferDirty(page->buffer);
+ 		}
+ 		END_CRIT_SECTION();
+ 	}
+ 	else
+ 	{
+ 		/* Unlogged relation: skip xlog-related stuff */
+ 		START_CRIT_SECTION();
+ 		for (i = 0; i < MAX_GENERIC_XLOG_PAGES; i++)
+ 		{
+ 			PageData   *page = &state->pages[i];
+ 
+ 			if (BufferIsInvalid(page->buffer))
+ 				continue;
+ 			memcpy(BufferGetPage(page->buffer), page->image, BLCKSZ);
+ 			MarkBufferDirty(page->buffer);
+ 		}
+ 		END_CRIT_SECTION();
+ 	}
+ 
+ 	return lsn;
+ }
+ 
+ /*
+  * Abort generic xlog record.
+  */
+ void
+ GenericXLogAbort(GenericXLogState *state)
+ {
+ 	pfree(state);
+ }
+ 
+ /*
+  * Apply delta to given page image.
+  */
+ static void
+ applyPageRedo(Page page, Pointer data, Size dataSize)
+ {
+ 	Pointer ptr = data, end = data + dataSize;
+ 
+ 	while (ptr < end)
+ 	{
+ 		OffsetNumber	offset,
+ 						length;
+ 
+ 		memcpy(&offset, ptr, sizeof(offset));
+ 		ptr += sizeof(offset);
+ 		memcpy(&length, ptr, sizeof(length));
+ 		ptr += sizeof(length);
+ 
+ 		memcpy(page + offset, ptr, length);
+ 
+ 		ptr += length;
+ 	}
+ }
+ 
+ /*
+  * Redo function for generic xlog record.
+  */
+ void
+ generic_redo(XLogReaderState *record)
+ {
+ 	uint8		block_id;
+ 	Buffer		buffers[MAX_GENERIC_XLOG_PAGES] = {InvalidBuffer};
+ 	XLogRecPtr	lsn = record->EndRecPtr;
+ 
+ 	Assert(record->max_block_id < MAX_GENERIC_XLOG_PAGES);
+ 
+ 	/* Iterate over blocks */
+ 	for (block_id = 0; block_id <= record->max_block_id; block_id++)
+ 	{
+ 		XLogRedoAction action;
+ 
+ 		if (!XLogRecHasBlockRef(record, block_id))
+ 			continue;
+ 
+ 		action = XLogReadBufferForRedo(record, block_id, &buffers[block_id]);
+ 
+ 		/* Apply redo to given block if needed */
+ 		if (action == BLK_NEEDS_REDO)
+ 		{
+ 			Pointer	blockData;
+ 			Size	blockDataSize;
+ 			Page	page;
+ 
+ 			page = BufferGetPage(buffers[block_id]);
+ 			blockData = XLogRecGetBlockData(record, block_id, &blockDataSize);
+ 			applyPageRedo(page, blockData, blockDataSize);
+ 
+ 			PageSetLSN(page, lsn);
+ 			MarkBufferDirty(buffers[block_id]);
+ 		}
+ 	}
+ 
+ 	/* Changes are done: unlock and release all buffers */
+ 	for (block_id = 0; block_id <= record->max_block_id; block_id++)
+ 	{
+ 		if (BufferIsValid(buffers[block_id]))
+ 			UnlockReleaseBuffer(buffers[block_id]);
+ 	}
+ }
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
new file mode 100644
index 7c4d773..7b38c16
*** a/src/backend/access/transam/rmgr.c
--- b/src/backend/access/transam/rmgr.c
***************
*** 11,16 ****
--- 11,17 ----
  #include "access/commit_ts.h"
  #include "access/gin.h"
  #include "access/gist_private.h"
+ #include "access/generic_xlog.h"
  #include "access/hash.h"
  #include "access/heapam_xlog.h"
  #include "access/brin_xlog.h"
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
new file mode 100644
index f0bc67c..7781ebc
*** a/src/backend/replication/logical/decode.c
--- b/src/backend/replication/logical/decode.c
*************** LogicalDecodingProcessRecord(LogicalDeco
*** 143,148 ****
--- 143,149 ----
  		case RM_BRIN_ID:
  		case RM_COMMIT_TS_ID:
  		case RM_REPLORIGIN_ID:
+ 		case RM_GENERIC_ID:
  			/* just deal with xid, and done */
  			ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(record),
  									buf.origptr);
diff --git a/src/bin/pg_xlogdump/.gitignore b/src/bin/pg_xlogdump/.gitignore
new file mode 100644
index eebaf30..33a1acf
*** a/src/bin/pg_xlogdump/.gitignore
--- b/src/bin/pg_xlogdump/.gitignore
***************
*** 4,9 ****
--- 4,10 ----
  /clogdesc.c
  /committsdesc.c
  /dbasedesc.c
+ /genericdesc.c
  /gindesc.c
  /gistdesc.c
  /hashdesc.c
diff --git a/src/bin/pg_xlogdump/rmgrdesc.c b/src/bin/pg_xlogdump/rmgrdesc.c
new file mode 100644
index f9cd395..cff7e59
*** a/src/bin/pg_xlogdump/rmgrdesc.c
--- b/src/bin/pg_xlogdump/rmgrdesc.c
***************
*** 11,16 ****
--- 11,17 ----
  #include "access/brin_xlog.h"
  #include "access/clog.h"
  #include "access/commit_ts.h"
+ #include "access/generic_xlog.h"
  #include "access/gin.h"
  #include "access/gist_private.h"
  #include "access/hash.h"
diff --git a/src/include/access/generic_xlog.h b/src/include/access/generic_xlog.h
new file mode 100644
index ...07c1fd6
*** a/src/include/access/generic_xlog.h
--- b/src/include/access/generic_xlog.h
***************
*** 0 ****
--- 1,40 ----
+ /*-------------------------------------------------------------------------
+  *
+  * generic_xlog.h
+  *	  Generic xlog API definition.
+  *
+  *
+  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/access/generic_xlog.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef GENERIC_XLOG_H
+ #define GENERIC_XLOG_H
+ 
+ #include "access/xlog.h"
+ #include "access/xlog_internal.h"
+ #include "storage/bufpage.h"
+ #include "utils/rel.h"
+ 
+ #define MAX_GENERIC_XLOG_PAGES	  3
+ 
+ /* state of generic xlog record construction */
+ struct GenericXLogState;
+ typedef struct GenericXLogState GenericXLogState;
+ 
+ /* API for construction of generic xlog records */
+ extern GenericXLogState *GenericXLogStart(Relation relation);
+ extern Page GenericXLogRegister(GenericXLogState *state, Buffer buffer, bool isNew);
+ extern void GenericXLogUnregister(GenericXLogState *state, Buffer buffer);
+ extern XLogRecPtr GenericXLogFinish(GenericXLogState *state);
+ extern void GenericXLogAbort(GenericXLogState *state);
+ 
+ /* functions defined for rmgr */
+ extern void generic_redo(XLogReaderState *record);
+ extern const char *generic_identify(uint8 info);
+ extern void generic_desc(StringInfo buf, XLogReaderState *record);
+ 
+ #endif   /* GENERIC_XLOG_H */
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
new file mode 100644
index fab912d..3cfe6f7
*** a/src/include/access/rmgrlist.h
--- b/src/include/access/rmgrlist.h
*************** PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo
*** 45,47 ****
--- 45,48 ----
  PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL)
  PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL)
  PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL)
+ PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL)
0003-bloom-contrib.15.patchapplication/octet-stream; name=0003-bloom-contrib.15.patchDownload
diff --git a/contrib/Makefile b/contrib/Makefile
new file mode 100644
index d12dd63..25263c0
*** a/contrib/Makefile
--- b/contrib/Makefile
*************** SUBDIRS = \
*** 8,13 ****
--- 8,14 ----
  		adminpack	\
  		auth_delay	\
  		auto_explain	\
+ 		bloom		\
  		btree_gin	\
  		btree_gist	\
  		chkpass		\
diff --git a/contrib/bloom/.gitignore b/contrib/bloom/.gitignore
new file mode 100644
index ...5dcb3ff
*** a/contrib/bloom/.gitignore
--- b/contrib/bloom/.gitignore
***************
*** 0 ****
--- 1,4 ----
+ # Generated subdirectories
+ /log/
+ /results/
+ /tmp_check/
diff --git a/contrib/bloom/Makefile b/contrib/bloom/Makefile
new file mode 100644
index ...13bd397
*** a/contrib/bloom/Makefile
--- b/contrib/bloom/Makefile
***************
*** 0 ****
--- 1,24 ----
+ # contrib/bloom/Makefile
+ 
+ MODULE_big = bloom
+ OBJS = blcost.o blinsert.o blscan.o blutils.o blvacuum.o blvalidate.o $(WIN32RES)
+ 
+ EXTENSION = bloom
+ DATA = bloom--1.0.sql
+ PGFILEDESC = "bloom access method - signature file based index"
+ 
+ REGRESS = bloom
+ 
+ ifdef USE_PGXS
+ PG_CONFIG = pg_config
+ PGXS := $(shell $(PG_CONFIG) --pgxs)
+ include $(PGXS)
+ else
+ subdir = contrib/bloom
+ top_builddir = ../..
+ include $(top_builddir)/src/Makefile.global
+ include $(top_srcdir)/contrib/contrib-global.mk
+ endif
+ 
+ wal-check: temp-install
+ 	$(prove_check)
diff --git a/contrib/bloom/blcost.c b/contrib/bloom/blcost.c
new file mode 100644
index ...9897898
*** a/contrib/bloom/blcost.c
--- b/contrib/bloom/blcost.c
***************
*** 0 ****
--- 1,48 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blcost.c
+  *		Cost estimate function for bloom indexes.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blcost.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "fmgr.h"
+ #include "optimizer/cost.h"
+ #include "utils/selfuncs.h"
+ 
+ #include "bloom.h"
+ 
+ /*
+  * Estimate cost of bloom index scan.
+  */
+ void
+ blcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
+ 			   Cost *indexStartupCost, Cost *indexTotalCost,
+ 			   Selectivity *indexSelectivity, double *indexCorrelation)
+ {
+ 	IndexOptInfo *index = path->indexinfo;
+ 	List	   *qinfos;
+ 	GenericCosts costs;
+ 
+ 	/* Do preliminary analysis of indexquals */
+ 	qinfos = deconstruct_indexquals(path);
+ 
+ 	MemSet(&costs, 0, sizeof(costs));
+ 
+ 	/* We have to visit all index tuples anyway */
+ 	costs.numIndexTuples = index->tuples;
+ 
+ 	/* Use generic estimate */
+ 	genericcostestimate(root, path, loop_count, qinfos, &costs);
+ 
+ 	*indexStartupCost = costs.indexStartupCost;
+ 	*indexTotalCost = costs.indexTotalCost;
+ 	*indexSelectivity = costs.indexSelectivity;
+ 	*indexCorrelation = costs.indexCorrelation;
+ }
diff --git a/contrib/bloom/blinsert.c b/contrib/bloom/blinsert.c
new file mode 100644
index ...09d2fd5
*** a/contrib/bloom/blinsert.c
--- b/contrib/bloom/blinsert.c
***************
*** 0 ****
--- 1,313 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blinsert.c
+  *		Bloom index build and insert functions.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blinsert.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "access/generic_xlog.h"
+ #include "catalog/index.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ 
+ #include "bloom.h"
+ 
+ PG_MODULE_MAGIC;
+ 
+ /*
+  * State of bloom index build.  We accumulate one page data here before
+  * flushing it to buffer manager.
+  */
+ typedef struct
+ {
+ 	BloomState	blstate;		/* bloom index state */
+ 	MemoryContext tmpCtx;		/* temporary memory context reset after
+ 								 * each tuple */
+ 	char		data[BLCKSZ];	/* cached page */
+ 	int64		count;			/* number of tuples in cached page */
+ }	BloomBuildState;
+ 
+ /*
+  * Flush page cached in BloomBuildState.
+  */
+ static void
+ flushCachedPage(Relation index, BloomBuildState *buildstate)
+ {
+ 	Page		page;
+ 	Buffer		buffer = BloomNewBuffer(index);
+ 	GenericXLogState *state;
+ 
+ 	state = GenericXLogStart(index);
+ 	page = GenericXLogRegister(state, buffer, true);
+ 	memcpy(page, buildstate->data, BLCKSZ);
+ 	GenericXLogFinish(state);
+ 	UnlockReleaseBuffer(buffer);
+ }
+ 
+ /*
+  * (Re)initialize cached page in BloomBuildState.
+  */
+ static void
+ initCachedPage(BloomBuildState *buildstate)
+ {
+ 	memset(buildstate->data, 0, BLCKSZ);
+ 	BloomInitPage(buildstate->data, 0);
+ 	buildstate->count = 0;
+ }
+ 
+ /*
+  * Per-tuple callback from IndexBuildHeapScan.
+  */
+ static void
+ bloomBuildCallback(Relation index, HeapTuple htup, Datum *values,
+ 				   bool *isnull, bool tupleIsAlive, void *state)
+ {
+ 	BloomBuildState *buildstate = (BloomBuildState *) state;
+ 	MemoryContext oldCtx;
+ 	BloomTuple *itup;
+ 
+ 	oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
+ 
+ 	itup = BloomFormTuple(&buildstate->blstate, &htup->t_self, values, isnull);
+ 
+ 	/* Try to add next item to cached page */
+ 	if (BloomPageAddItem(&buildstate->blstate, buildstate->data, itup))
+ 	{
+ 		/* Next item was added successfully */
+ 		buildstate->count++;
+ 	}
+ 	else
+ 	{
+ 		/* Cached page is full, flush it out and make a new one */
+ 		flushCachedPage(index, buildstate);
+ 
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		initCachedPage(buildstate);
+ 
+ 		if (BloomPageAddItem(&buildstate->blstate, buildstate->data, itup) == false)
+ 		{
+ 			/* We shouldn't be here since we're inserting to the empty page */
+ 			elog(ERROR, "can not add new tuple");
+ 		}
+ 	}
+ 
+ 	MemoryContextSwitchTo(oldCtx);
+ 	MemoryContextReset(buildstate->tmpCtx);
+ }
+ 
+ /*
+  * Build a new bloom index.
+  */
+ IndexBuildResult *
+ blbuild(Relation heap, Relation index, IndexInfo *indexInfo)
+ {
+ 	IndexBuildResult *result;
+ 	double		reltuples;
+ 	BloomBuildState buildstate;
+ 
+ 	if (RelationGetNumberOfBlocks(index) != 0)
+ 		elog(ERROR, "index \"%s\" already contains data",
+ 			 RelationGetRelationName(index));
+ 
+ 	/* Initialize the meta page */
+ 	BloomInitMetapage(index);
+ 
+ 	/* Initialize the bloom build state */
+ 	memset(&buildstate, 0, sizeof(buildstate));
+ 	initBloomState(&buildstate.blstate, index);
+ 	buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
+ 											  "Bloom build temporary context",
+ 											  ALLOCSET_DEFAULT_MINSIZE,
+ 											  ALLOCSET_DEFAULT_INITSIZE,
+ 											  ALLOCSET_DEFAULT_MAXSIZE);
+ 	initCachedPage(&buildstate);
+ 
+ 	/* Do the heap scan */
+ 	reltuples = IndexBuildHeapScan(heap, index, indexInfo, true,
+ 								   bloomBuildCallback, (void *) &buildstate);
+ 
+ 	/*
+ 	 * There are could be some items in cached page.  Flush this page
+ 	 * if needed.
+ 	 */
+ 	if (buildstate.count > 0)
+ 		flushCachedPage(index, &buildstate);
+ 
+ 	MemoryContextDelete(buildstate.tmpCtx);
+ 
+ 	result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
+ 	result->heap_tuples = result->index_tuples = reltuples;
+ 
+ 	return result;
+ }
+ 
+ /*
+  * Build an empty bloom index in the initialization fork.
+  */
+ void
+ blbuildempty(Relation index)
+ {
+ 	if (RelationGetNumberOfBlocks(index) != 0)
+ 		elog(ERROR, "index \"%s\" already contains data",
+ 			 RelationGetRelationName(index));
+ 
+ 	/* Initialize the meta page */
+ 	BloomInitMetapage(index);
+ }
+ 
+ /*
+  * Insert new tuple to the bloom index.
+  */
+ bool
+ blinsert(Relation index, Datum *values, bool *isnull,
+ 		 ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique)
+ {
+ 	BloomState	blstate;
+ 	BloomTuple *itup;
+ 	MemoryContext oldCtx;
+ 	MemoryContext insertCtx;
+ 	BloomMetaPageData *metaData;
+ 	Buffer		buffer,
+ 				metaBuffer;
+ 	Page		page,
+ 				metaPage;
+ 	BlockNumber blkno = InvalidBlockNumber;
+ 	OffsetNumber nStart;
+ 	GenericXLogState *state;
+ 
+ 	insertCtx = AllocSetContextCreate(CurrentMemoryContext,
+ 									  "Bloom insert temporary context",
+ 									  ALLOCSET_DEFAULT_MINSIZE,
+ 									  ALLOCSET_DEFAULT_INITSIZE,
+ 									  ALLOCSET_DEFAULT_MAXSIZE);
+ 
+ 	oldCtx = MemoryContextSwitchTo(insertCtx);
+ 
+ 	initBloomState(&blstate, index);
+ 	itup = BloomFormTuple(&blstate, ht_ctid, values, isnull);
+ 
+ 	/*
+ 	 * At first, try to insert new tuple to the first page in notFullPage
+ 	 * array.  If success we don't need to modify the meta page.
+ 	 */
+ 	metaBuffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 	LockBuffer(metaBuffer, BUFFER_LOCK_SHARE);
+ 	metaData = BloomPageGetMeta(BufferGetPage(metaBuffer));
+ 
+ 	if (metaData->nEnd > metaData->nStart)
+ 	{
+ 		Page		page;
+ 
+ 		blkno = metaData->notFullPage[metaData->nStart];
+ 
+ 		Assert(blkno != InvalidBlockNumber);
+ 		LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		state = GenericXLogStart(index);
+ 		page = GenericXLogRegister(state, buffer, false);
+ 
+ 		if (BloomPageAddItem(&blstate, page, itup))
+ 		{
+ 			GenericXLogFinish(state);
+ 			UnlockReleaseBuffer(buffer);
+ 			ReleaseBuffer(metaBuffer);
+ 			goto away;
+ 		}
+ 		else
+ 		{
+ 			GenericXLogAbort(state);
+ 			UnlockReleaseBuffer(buffer);
+ 		}
+ 	}
+ 	else
+ 	{
+ 		/* First page in notFullPage isn't suitable */
+ 		LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
+ 	}
+ 
+ 	/*
+ 	 * Try other pages in notFullPage array.  We will have to change nStart in
+ 	 * metapage.  Thus, grab exclusive lock on metapage.
+ 	 */
+ 	LockBuffer(metaBuffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 	state = GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(state, metaBuffer, false);
+ 	metaData = BloomPageGetMeta(metaPage);
+ 
+ 	/*
+ 	 * Iterate over notFullPage array.  Skip page we already tried first.
+ 	 */
+ 	nStart = metaData->nStart;
+ 	if (metaData->nEnd > nStart &&
+ 		blkno == metaData->notFullPage[nStart])
+ 		nStart++;
+ 
+ 	while (metaData->nEnd > nStart)
+ 	{
+ 		blkno = metaData->notFullPage[nStart];
+ 		Assert(blkno != InvalidBlockNumber);
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		page = GenericXLogRegister(state, buffer, false);
+ 
+ 		if (BloomPageAddItem(&blstate, page, itup))
+ 		{
+ 			metaData->nStart = nStart;
+ 			GenericXLogFinish(state);
+ 			UnlockReleaseBuffer(buffer);
+ 			UnlockReleaseBuffer(metaBuffer);
+ 			goto away;
+ 		}
+ 		else
+ 		{
+ 			GenericXLogUnregister(state, buffer);
+ 			UnlockReleaseBuffer(buffer);
+ 		}
+ 		nStart++;
+ 	}
+ 
+ 	GenericXLogAbort(state);
+ 
+ 	/*
+ 	 * Didn't find place to insert in notFullPage array.  Allocate new page.
+ 	 */
+ 	buffer = BloomNewBuffer(index);
+ 
+ 	state = GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(state, metaBuffer, false);
+ 	metaData = BloomPageGetMeta(metaPage);
+ 	page = GenericXLogRegister(state, buffer, true);
+ 	BloomInitPage(page, 0);
+ 	BloomPageAddItem(&blstate, page, itup);
+ 
+ 	metaData->nStart = 0;
+ 	metaData->nEnd = 1;
+ 	metaData->notFullPage[0] = BufferGetBlockNumber(buffer);
+ 
+ 	GenericXLogFinish(state);
+ 
+ 	UnlockReleaseBuffer(buffer);
+ 	UnlockReleaseBuffer(metaBuffer);
+ 
+ away:
+ 	MemoryContextSwitchTo(oldCtx);
+ 	MemoryContextDelete(insertCtx);
+ 
+ 	return false;
+ }
diff --git a/contrib/bloom/bloom--1.0.sql b/contrib/bloom/bloom--1.0.sql
new file mode 100644
index ...7fa7513
*** a/contrib/bloom/bloom--1.0.sql
--- b/contrib/bloom/bloom--1.0.sql
***************
*** 0 ****
--- 1,19 ----
+ CREATE OR REPLACE FUNCTION blhandler(internal)
+ RETURNS index_am_handler
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C;
+ 
+ -- Access method
+ CREATE ACCESS METHOD bloom TYPE INDEX HANDLER blhandler;
+ 
+ -- Opclasses
+ 
+ CREATE OPERATOR CLASS int4_ops
+ DEFAULT FOR TYPE int4 USING bloom AS
+ 	OPERATOR	1	=(int4, int4),
+ 	FUNCTION	1	hashint4(int4);
+ 
+ CREATE OPERATOR CLASS text_ops
+ DEFAULT FOR TYPE text USING bloom AS
+ 	OPERATOR	1	=(text, text),
+ 	FUNCTION	1	hashtext(text);
diff --git a/contrib/bloom/bloom.control b/contrib/bloom/bloom.control
new file mode 100644
index ...4d4124b
*** a/contrib/bloom/bloom.control
--- b/contrib/bloom/bloom.control
***************
*** 0 ****
--- 1,5 ----
+ # bloom extension
+ comment = 'bloom access method - signature file based index'
+ default_version = '1.0'
+ module_pathname = '$libdir/bloom'
+ relocatable = true
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
new file mode 100644
index ...50bf99b
*** a/contrib/bloom/bloom.h
--- b/contrib/bloom/bloom.h
***************
*** 0 ****
--- 1,178 ----
+ /*-------------------------------------------------------------------------
+  *
+  * bloom.h
+  *	  Header for bloom index.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/bloom.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef _BLOOM_H_
+ #define _BLOOM_H_
+ 
+ #include "access/amapi.h"
+ #include "access/generic_xlog.h"
+ #include "access/itup.h"
+ #include "access/xlog.h"
+ #include "nodes/relation.h"
+ #include "fmgr.h"
+ 
+ /* Support procedures numbers */
+ #define BLOOM_HASH_PROC			1
+ #define BLOOM_NPROC				1
+ 
+ /* Scan strategies */
+ #define BLOOM_EQUAL_STRATEGY	1
+ #define BLOOM_NSTRATEGIES		1
+ 
+ /* Opaque for bloom pages */
+ typedef struct BloomPageOpaqueData
+ {
+ 	OffsetNumber maxoff;
+ 	uint16		flags;
+ }	BloomPageOpaqueData;
+ 
+ typedef BloomPageOpaqueData *BloomPageOpaque;
+ 
+ /* Bloom page flags */
+ #define BLOOM_META		(1<<0)
+ #define BLOOM_DELETED	(2<<0)
+ 
+ /* Macros for accessing bloom page structures */
+ #define BloomPageGetOpaque(page) ((BloomPageOpaque) PageGetSpecialPointer(page))
+ #define BloomPageGetMaxOffset(page) (BloomPageGetOpaque(page)->maxoff)
+ #define BloomPageIsMeta(page) (BloomPageGetOpaque(page)->flags & BLOOM_META)
+ #define BloomPageIsDeleted(page) (BloomPageGetOpaque(page)->flags & BLOOM_DELETED)
+ #define BloomPageSetDeleted(page) (BloomPageGetOpaque(page)->flags |= BLOOM_DELETED)
+ #define BloomPageSetNonDeleted(page) (BloomPageGetOpaque(page)->flags &= ~BLOOM_DELETED)
+ #define BloomPageGetData(page)		((BloomTuple *)PageGetContents(page))
+ #define BloomPageGetTuple(state, page, offset) \
+ 	((BloomTuple *)(PageGetContents(page) \
+ 		+ (state)->sizeOfBloomTuple * ((offset) - 1)))
+ #define BloomPageGetNextTuple(state, tuple) \
+ 	((BloomTuple *)((Pointer)(tuple) + (state)->sizeOfBloomTuple))
+ 
+ /* Preserved page numbers */
+ #define BLOOM_METAPAGE_BLKNO	(0)
+ #define BLOOM_HEAD_BLKNO		(1)		/* first data page */
+ 
+ /* Bloom index options */
+ typedef struct BloomOptions
+ {
+ 	int32		vl_len_;		/* varlena header (do not touch directly!) */
+ 	int			bloomLength;	/* length of signature in uint16 */
+ 	int			bitSize[INDEX_MAX_KEYS];		/* signature bits per index
+ 												 * key */
+ }	BloomOptions;
+ 
+ /*
+  * FreeBlockNumberArray - array of block numbers sized so that metadata fill
+  * all space in metapage.
+  */
+ typedef BlockNumber FreeBlockNumberArray[
+ 										 MAXALIGN_DOWN(
+ 		BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(BloomPageOpaqueData))
+ 	   - MAXALIGN(sizeof(uint16) * 2 + sizeof(uint32) + sizeof(BloomOptions))
+ 													   ) / sizeof(BlockNumber)
+ ];
+ 
+ /* Metadata of bloom index */
+ typedef struct BloomMetaPageData
+ {
+ 	uint32		magickNumber;
+ 	uint16		nStart;
+ 	uint16		nEnd;
+ 	BloomOptions opts;
+ 	FreeBlockNumberArray notFullPage;
+ }	BloomMetaPageData;
+ 
+ /* Magic number to distinguish bloom pages among anothers */
+ #define BLOOM_MAGICK_NUMBER (0xDBAC0DED)
+ 
+ /* Number of blocks numbers fit in BloomMetaPageData */
+ #define BloomMetaBlockN		(sizeof(FreeBlockNumberArray) / sizeof(BlockNumber))
+ 
+ #define BloomPageGetMeta(page)	((BloomMetaPageData *) PageGetContents(page))
+ 
+ typedef struct BloomState
+ {
+ 	FmgrInfo	hashFn[INDEX_MAX_KEYS];
+ 	BloomOptions *opts;			/* stored in rd_amcache and defined at
+ 								 * creation time */
+ 	int32		nColumns;
+ 
+ 	/*
+ 	 * sizeOfBloomTuple is index's specific, and it depends on reloptions, so
+ 	 * precompute it
+ 	 */
+ 	int32		sizeOfBloomTuple;
+ }	BloomState;
+ 
+ #define BloomPageGetFreeSpace(state, page) \
+ 	(BLCKSZ - MAXALIGN(SizeOfPageHeaderData) \
+ 		- BloomPageGetMaxOffset(page) * (state)->sizeOfBloomTuple \
+ 		- MAXALIGN(sizeof(BloomPageOpaqueData)))
+ 
+ /*
+  * Tuples are very different from all other relations
+  */
+ typedef uint16 SignType;
+ 
+ typedef struct BloomTuple
+ {
+ 	ItemPointerData heapPtr;
+ 	SignType	sign[1];
+ }	BloomTuple;
+ 
+ #define BLOOMTUPLEHDRSZ offsetof(BloomTuple, sign)
+ 
+ /* Opaque data structure for bloom index scan */
+ typedef struct BloomScanOpaqueData
+ {
+ 	SignType   *sign;			/* Scan signature */
+ 	BloomState	state;
+ }	BloomScanOpaqueData;
+ 
+ typedef BloomScanOpaqueData *BloomScanOpaque;
+ 
+ /* blutils.c */
+ extern void _PG_init(void);
+ extern Datum blhandler(PG_FUNCTION_ARGS);
+ extern void initBloomState(BloomState * state, Relation index);
+ extern void BloomInitMetapage(Relation index);
+ extern void BloomInitPage(Page page, uint16 flags);
+ extern Buffer BloomNewBuffer(Relation index);
+ extern void signValue(BloomState * state, SignType * sign, Datum value, int attno);
+ extern BloomTuple *BloomFormTuple(BloomState * state, ItemPointer iptr, Datum *values, bool *isnull);
+ extern bool BloomPageAddItem(BloomState * state, Page page, BloomTuple * tuple);
+ 
+ /* blvalidate.c */
+ extern bool blvalidate(Oid opclassoid);
+ 
+ /* index access method interface functions */
+ extern bool blinsert(Relation index, Datum *values, bool *isnull,
+ 		 ItemPointer ht_ctid, Relation heapRel,
+ 		 IndexUniqueCheck checkUnique);
+ extern IndexScanDesc blbeginscan(Relation r, int nkeys, int norderbys);
+ extern int64 blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
+ extern void blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ 		 ScanKey orderbys, int norderbys);
+ extern void blendscan(IndexScanDesc scan);
+ extern IndexBuildResult *blbuild(Relation heap, Relation index,
+ 		struct IndexInfo *indexInfo);
+ extern void blbuildempty(Relation index);
+ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
+ 			 IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback,
+ 			 void *callback_state);
+ extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
+ 				IndexBulkDeleteResult *stats);
+ extern bytea *bloptions(Datum reloptions, bool validate);
+ extern void blcostestimate(PlannerInfo *root, IndexPath *path,
+ 			   double loop_count, Cost *indexStartupCost,
+ 			   Cost *indexTotalCost, Selectivity *indexSelectivity,
+ 			   double *indexCorrelation);
+ 
+ #endif
diff --git a/contrib/bloom/blscan.c b/contrib/bloom/blscan.c
new file mode 100644
index ...d156e88
*** a/contrib/bloom/blscan.c
--- b/contrib/bloom/blscan.c
***************
*** 0 ****
--- 1,175 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blscan.c
+  *		Bloom index scan functions.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blscan.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/relscan.h"
+ #include "pgstat.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/lmgr.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ 
+ #include "bloom.h"
+ 
+ /*
+  * Begin scan of bloom index.
+  */
+ IndexScanDesc
+ blbeginscan(Relation r, int nkeys, int norderbys)
+ {
+ 	IndexScanDesc scan;
+ 
+ 	scan = RelationGetIndexScan(r, nkeys, norderbys);
+ 
+ 	return scan;
+ }
+ 
+ /*
+  * Rescan a bloom index.
+  */
+ void
+ blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ 		 ScanKey orderbys, int norderbys)
+ {
+ 	BloomScanOpaque so;
+ 
+ 	so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so == NULL)
+ 	{
+ 		/* if called from blbeginscan */
+ 		so = (BloomScanOpaque) palloc(sizeof(BloomScanOpaqueData));
+ 		initBloomState(&so->state, scan->indexRelation);
+ 		scan->opaque = so;
+ 
+ 	}
+ 	else
+ 	{
+ 		if (so->sign)
+ 			pfree(so->sign);
+ 	}
+ 	so->sign = NULL;
+ 
+ 	if (scankey && scan->numberOfKeys > 0)
+ 	{
+ 		memmove(scan->keyData, scankey,
+ 				scan->numberOfKeys * sizeof(ScanKeyData));
+ 	}
+ }
+ 
+ /*
+  * End scan of bloom index.
+  */
+ void
+ blendscan(IndexScanDesc scan)
+ {
+ 	BloomScanOpaque so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so->sign)
+ 		pfree(so->sign);
+ 	so->sign = NULL;
+ }
+ 
+ /*
+  * Insert all matching tuples into to a bitmap.
+  */
+ int64
+ blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
+ {
+ 	int64		ntids = 0;
+ 	BlockNumber blkno = BLOOM_HEAD_BLKNO,
+ 				npages;
+ 	int			i;
+ 	BufferAccessStrategy bas;
+ 	BloomScanOpaque so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so->sign == NULL && scan->numberOfKeys > 0)
+ 	{
+ 		/* New search: have to calculate search signature */
+ 		ScanKey		skey = scan->keyData;
+ 
+ 		so->sign = palloc0(sizeof(SignType) * so->state.opts->bloomLength);
+ 
+ 		for (i = 0; i < scan->numberOfKeys; i++)
+ 		{
+ 			/*
+ 			 * Assume bloom-indexable operators to be strict, so nothing could
+ 			 * be found for NULL key.
+ 			 */
+ 			if (skey->sk_flags & SK_ISNULL)
+ 			{
+ 				pfree(so->sign);
+ 				so->sign = NULL;
+ 				return 0;
+ 			}
+ 
+ 			/* Add next value to the signature */
+ 			signValue(&so->state, so->sign, skey->sk_argument,
+ 					  skey->sk_attno - 1);
+ 
+ 			skey++;
+ 		}
+ 	}
+ 
+ 	/*
+ 	 * We're going to read the whole index. This is why we use appropriate
+ 	 * buffer access strategy.
+ 	 */
+ 	bas = GetAccessStrategy(BAS_BULKREAD);
+ 	npages = RelationGetNumberOfBlocks(scan->indexRelation);
+ 
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		Buffer		buffer;
+ 		Page		page;
+ 
+ 		buffer = ReadBufferExtended(scan->indexRelation, MAIN_FORKNUM,
+ 									blkno, RBM_NORMAL, bas);
+ 
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 		page = BufferGetPage(buffer);
+ 
+ 		if (!BloomPageIsDeleted(page))
+ 		{
+ 			OffsetNumber offset,
+ 						maxOffset = BloomPageGetMaxOffset(page);
+ 
+ 			for (offset = 1; offset <= maxOffset; offset++)
+ 			{
+ 				BloomTuple *itup = BloomPageGetTuple(&so->state, page, offset);
+ 				bool		res = true;
+ 
+ 				/* Check index signature with scan signature */
+ 				for (i = 0; res && i < so->state.opts->bloomLength; i++)
+ 				{
+ 					if ((itup->sign[i] & so->sign[i]) != so->sign[i])
+ 						res = false;
+ 				}
+ 
+ 				/* Add matching tuples to bitmap */
+ 				if (res)
+ 				{
+ 					tbm_add_tuples(tbm, &itup->heapPtr, 1, true);
+ 					ntids++;
+ 				}
+ 			}
+ 		}
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 		CHECK_FOR_INTERRUPTS();
+ 	}
+ 	FreeAccessStrategy(bas);
+ 
+ 	return ntids;
+ }
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
new file mode 100644
index ...576aaca
*** a/contrib/bloom/blutils.c
--- b/contrib/bloom/blutils.c
***************
*** 0 ****
--- 1,417 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blutils.c
+  *		Bloom index utilities.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blutils.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/amapi.h"
+ #include "access/generic_xlog.h"
+ #include "catalog/index.h"
+ #include "storage/lmgr.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "utils/memutils.h"
+ #include "access/reloptions.h"
+ #include "storage/freespace.h"
+ #include "storage/indexfsm.h"
+ 
+ #include "bloom.h"
+ 
+ /* Signature dealing macros */
+ #define BITSIGNTYPE (BITS_PER_BYTE * sizeof(SignType))
+ #define GETWORD(x,i) ( *( (SignType*)(x) + (int)( (i) / BITSIGNTYPE ) ) )
+ #define CLRBIT(x,i)   GETWORD(x,i) &= ~( 0x01 << ( (i) % BITSIGNTYPE ) )
+ #define SETBIT(x,i)   GETWORD(x,i) |=  ( 0x01 << ( (i) % BITSIGNTYPE ) )
+ #define GETBIT(x,i) ( (GETWORD(x,i) >> ( (i) % BITSIGNTYPE )) & 0x01 )
+ 
+ PG_FUNCTION_INFO_V1(blhandler);
+ 
+ /* Kind of relation optioms for bloom index */
+ static relopt_kind bl_relopt_kind;
+ 
+ /*
+  * Module initialize function: initilized relation options.
+  */
+ void
+ _PG_init(void)
+ {
+ 	int			i;
+ 	char		buf[16];
+ 
+ 	bl_relopt_kind = add_reloption_kind();
+ 
+ 	add_int_reloption(bl_relopt_kind, "length",
+ 					  "Length of signature in uint16 type", 5, 1, 256);
+ 
+ 	for (i = 0; i < INDEX_MAX_KEYS; i++)
+ 	{
+ 		snprintf(buf, 16, "col%d", i + 1);
+ 		add_int_reloption(bl_relopt_kind, buf,
+ 					  "Number of bits for corresponding column", 2, 1, 2048);
+ 	}
+ }
+ 
+ /*
+  * Bloom handler function: return IndexAmRoutine with access method parameters
+  * and callbacks.
+  */
+ Datum
+ blhandler(PG_FUNCTION_ARGS)
+ {
+ 	IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
+ 
+ 	amroutine->amstrategies = 1;
+ 	amroutine->amsupport = 1;
+ 	amroutine->amcanorder = false;
+ 	amroutine->amcanorderbyop = false;
+ 	amroutine->amcanbackward = false;
+ 	amroutine->amcanunique = false;
+ 	amroutine->amcanmulticol = true;
+ 	amroutine->amoptionalkey = true;
+ 	amroutine->amsearcharray = false;
+ 	amroutine->amsearchnulls = false;
+ 	amroutine->amstorage = false;
+ 	amroutine->amclusterable = false;
+ 	amroutine->ampredlocks = false;
+ 	amroutine->amkeytype = 0;
+ 
+ 	amroutine->aminsert = blinsert;
+ 	amroutine->ambeginscan = blbeginscan;
+ 	amroutine->amgettuple = NULL;
+ 	amroutine->amgetbitmap = blgetbitmap;
+ 	amroutine->amrescan = blrescan;
+ 	amroutine->amendscan = blendscan;
+ 	amroutine->ammarkpos = NULL;
+ 	amroutine->amrestrpos = NULL;
+ 	amroutine->ambuild = blbuild;
+ 	amroutine->ambuildempty = blbuildempty;
+ 	amroutine->ambulkdelete = blbulkdelete;
+ 	amroutine->amvacuumcleanup = blvacuumcleanup;
+ 	amroutine->amcanreturn = NULL;
+ 	amroutine->amcostestimate = blcostestimate;
+ 	amroutine->amoptions = bloptions;
+ 	amroutine->amvalidate = blvalidate;
+ 
+ 	PG_RETURN_POINTER(amroutine);
+ }
+ 
+ /*
+  * Fill BloomState structure for particular index.
+  */
+ void
+ initBloomState(BloomState *state, Relation index)
+ {
+ 	int			i;
+ 
+ 	state->nColumns = index->rd_att->natts;
+ 
+ 	/* Initialize hash function for each attribute */
+ 	for (i = 0; i < index->rd_att->natts; i++)
+ 	{
+ 		fmgr_info_copy(&(state->hashFn[i]),
+ 					   index_getprocinfo(index, i + 1, BLOOM_HASH_PROC),
+ 					   CurrentMemoryContext);
+ 	}
+ 
+ 	/* Initialize amcache if needed with options from metapage */
+ 	if (!index->rd_amcache)
+ 	{
+ 		Buffer		buffer;
+ 		Page		page;
+ 		BloomMetaPageData *meta;
+ 		BloomOptions *opts;
+ 
+ 		opts = MemoryContextAlloc(index->rd_indexcxt, sizeof(BloomOptions));
+ 
+ 		buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 
+ 		page = BufferGetPage(buffer);
+ 
+ 		if (!BloomPageIsMeta(page))
+ 			elog(ERROR, "Relation is not a bloom index");
+ 		meta = BloomPageGetMeta(BufferGetPage(buffer));
+ 
+ 		if (meta->magickNumber != BLOOM_MAGICK_NUMBER)
+ 			elog(ERROR, "Relation is not a bloom index");
+ 
+ 		*opts = meta->opts;
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 
+ 		index->rd_amcache = (void *) opts;
+ 	}
+ 
+ 	state->opts = (BloomOptions *) index->rd_amcache;
+ 	state->sizeOfBloomTuple = BLOOMTUPLEHDRSZ +
+ 		sizeof(SignType) * state->opts->bloomLength;
+ }
+ 
+ /*
+  * Add bits of given value to the signature.
+  */
+ void
+ signValue(BloomState *state, SignType *sign, Datum value, int attno)
+ {
+ 	uint32		hashVal;
+ 	int			nBit,
+ 				j;
+ 
+ 	/*
+ 	 * init generator with "column's" number to get "hashed" seed for new
+ 	 * value. We don't want to map the same numbers from different columns
+ 	 * into the same bits!
+ 	 */
+ 	srand(attno);
+ 
+ 	/*
+ 	 * Init hash sequence to map our value into bits. the same values in
+ 	 * different columns will be mapped into different bits because of step
+ 	 * above
+ 	 */
+ 	hashVal = DatumGetInt32(FunctionCall1(&state->hashFn[attno], value));
+ 	srand(hashVal ^ rand());
+ 
+ 	for (j = 0; j < state->opts->bitSize[attno]; j++)
+ 	{
+ 		/* prevent mutiple evaluation */
+ 		nBit = rand() % (state->opts->bloomLength * BITSIGNTYPE);
+ 		SETBIT(sign, nBit);
+ 	}
+ }
+ 
+ /*
+  * Make bloom tuple from values.
+  */
+ BloomTuple *
+ BloomFormTuple(BloomState *state, ItemPointer iptr, Datum *values, bool *isnull)
+ {
+ 	int			i;
+ 	BloomTuple *res = (BloomTuple *) palloc0(state->sizeOfBloomTuple);
+ 
+ 	res->heapPtr = *iptr;
+ 
+ 	/* Blooming each column */
+ 	for (i = 0; i < state->nColumns; i++)
+ 	{
+ 		/* skip nulls */
+ 		if (isnull[i])
+ 			continue;
+ 
+ 		signValue(state, res->sign, values[i], i);
+ 	}
+ 
+ 	return res;
+ }
+ 
+ /*
+  * Add new bloom tuple to the page.  Returns true if new tuple was successfully
+  * added to the page.  Returns false if it doesn't git the page.
+  */
+ bool
+ BloomPageAddItem(BloomState *state, Page page, BloomTuple *tuple)
+ {
+ 	BloomTuple *itup;
+ 	BloomPageOpaque opaque;
+ 	Pointer		ptr;
+ 
+ 	/* Does new tuple fit the page */
+ 	if (BloomPageGetFreeSpace(state, page) < state->sizeOfBloomTuple)
+ 		return false;
+ 
+ 	/* Copy new tuple to the end of page */
+ 	opaque = BloomPageGetOpaque(page);
+ 	itup = BloomPageGetTuple(state, page, opaque->maxoff + 1);
+ 	memcpy((Pointer) itup, (Pointer) tuple, state->sizeOfBloomTuple);
+ 
+ 	/* Adjust maxoff and pd_lower */
+ 	opaque->maxoff++;
+ 	ptr = (Pointer) BloomPageGetTuple(state, page, opaque->maxoff + 1);
+ 	((PageHeader) page)->pd_lower = ptr - page;
+ 
+ 	return true;
+ }
+ 
+ /*
+  * Allocate a new page (either by recycling, or by extending the index file)
+  * The returned buffer is already pinned and exclusive-locked
+  * Caller is responsible for initializing the page by calling BloomInitBuffer
+  */
+ Buffer
+ BloomNewBuffer(Relation index)
+ {
+ 	Buffer		buffer;
+ 	bool		needLock;
+ 
+ 	/* First, try to get a page from FSM */
+ 	for (;;)
+ 	{
+ 		BlockNumber blkno = GetFreeIndexPage(index);
+ 
+ 		if (blkno == InvalidBlockNumber)
+ 			break;
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 
+ 		/*
+ 		 * We have to guard against the possibility that someone else already
+ 		 * recycled this page; the buffer may be locked if so.
+ 		 */
+ 		if (ConditionalLockBuffer(buffer))
+ 		{
+ 			Page		page = BufferGetPage(buffer);
+ 
+ 			if (PageIsNew(page))
+ 				return buffer;	/* OK to use, if never initialized */
+ 
+ 			if (BloomPageIsDeleted(page))
+ 				return buffer;	/* OK to use */
+ 
+ 			LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ 		}
+ 
+ 		/* Can't use it, so release buffer and try again */
+ 		ReleaseBuffer(buffer);
+ 	}
+ 
+ 	/* Must extend the file */
+ 	needLock = !RELATION_IS_LOCAL(index);
+ 	if (needLock)
+ 		LockRelationForExtension(index, ExclusiveLock);
+ 
+ 	buffer = ReadBuffer(index, P_NEW);
+ 	LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 	if (needLock)
+ 		UnlockRelationForExtension(index, ExclusiveLock);
+ 
+ 	return buffer;
+ }
+ 
+ /*
+  * Initialize bloom page.
+  */
+ void
+ BloomInitPage(Page page, uint16 flags)
+ {
+ 	BloomPageOpaque opaque;
+ 
+ 	PageInit(page, BLCKSZ, sizeof(BloomPageOpaqueData));
+ 
+ 	opaque = BloomPageGetOpaque(page);
+ 	memset(opaque, 0, sizeof(BloomPageOpaqueData));
+ 	opaque->maxoff = 0;
+ 	opaque->flags = flags;
+ }
+ 
+ /*
+  * Adjust options of bloom index.
+  */
+ static void
+ adjustBloomOptions(BloomOptions *opts)
+ {
+ 	int				i;
+ 
+ 	/* Default length of bloom filter is 5 of 16-bit integers */
+ 	if (opts->bloomLength <= 0)
+ 		opts->bloomLength = 5;
+ 	else
+ 		opts->bloomLength = opts->bloomLength;
+ 
+ 	/* Check singnature length */
+ 	for (i = 0; i < INDEX_MAX_KEYS; i++)
+ 	{
+ 		/*
+ 		 * Zero and negative number of bits is meaningless.  Also setting
+ 		 * more bits than signature have seems useless.  Replace both cases
+ 		 * with 2 bits default.
+ 		 */
+ 		if (opts->bitSize[i] <= 0
+ 			|| opts->bitSize[i] >= opts->bloomLength * sizeof(SignType))
+ 			opts->bitSize[i] = 2;
+ 	}
+ }
+ 
+ /*
+  * Initialize metapage for bloom index.
+  */
+ void
+ BloomInitMetapage(Relation index)
+ {
+ 	Page		metaPage;
+ 	Buffer		metaBuffer;
+ 	BloomMetaPageData *metadata;
+ 	GenericXLogState *state;
+ 
+ 	/*
+ 	 * Make a new buffer, since it first buffer it should be associated with
+ 	 * block number 0 (BLOOM_METAPAGE_BLKNO).
+ 	 */
+ 	metaBuffer = BloomNewBuffer(index);
+ 	Assert(BufferGetBlockNumber(metaBuffer) == BLOOM_METAPAGE_BLKNO);
+ 
+ 	/* Initialize bloom index options */
+ 	if (!index->rd_options)
+ 		index->rd_options = palloc0(sizeof(BloomOptions));
+ 	adjustBloomOptions((BloomOptions *) index->rd_options);
+ 
+ 	/* Initialize contents of meta page */
+ 	state = GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(state, metaBuffer, true);
+ 
+ 	BloomInitPage(metaPage, BLOOM_META);
+ 	metadata = BloomPageGetMeta(metaPage);
+ 	memset(metadata, 0, sizeof(BloomMetaPageData));
+ 	metadata->magickNumber = BLOOM_MAGICK_NUMBER;
+ 	metadata->opts = *((BloomOptions *) index->rd_options);
+ 	((PageHeader) metaPage)->pd_lower += sizeof(BloomMetaPageData);
+ 
+ 	GenericXLogFinish(state);
+ 	UnlockReleaseBuffer(metaBuffer);
+ }
+ 
+ /*
+  * Initialize options for bloom index.
+  */
+ bytea *
+ bloptions(Datum reloptions, bool validate)
+ {
+ 	relopt_value *options;
+ 	int			numoptions;
+ 	BloomOptions *rdopts;
+ 	relopt_parse_elt tab[INDEX_MAX_KEYS + 1];
+ 	int			i;
+ 	char		buf[16];
+ 
+ 	/* Option for length of signature */
+ 	tab[0].optname = "length";
+ 	tab[0].opttype = RELOPT_TYPE_INT;
+ 	tab[0].offset = offsetof(BloomOptions, bloomLength);
+ 
+ 	/* Number of bits for each of possible columns: col1, col2, ... */
+ 	for (i = 0; i < INDEX_MAX_KEYS; i++)
+ 	{
+ 		snprintf(buf, sizeof(buf), "col%d", i + 1);
+ 		tab[i + 1].optname = pstrdup(buf);
+ 		tab[i + 1].opttype = RELOPT_TYPE_INT;
+ 		tab[i + 1].offset = offsetof(BloomOptions, bitSize[i]);
+ 	}
+ 
+ 	options = parseRelOptions(reloptions, validate, bl_relopt_kind, &numoptions);
+ 	rdopts = allocateReloptStruct(sizeof(BloomOptions), options, numoptions);
+ 	fillRelOptions((void *) rdopts, sizeof(BloomOptions), options, numoptions,
+ 				   validate, tab, INDEX_MAX_KEYS + 1);
+ 
+ 	adjustBloomOptions(rdopts);
+ 
+ 	return (bytea *) rdopts;
+ }
diff --git a/contrib/bloom/blvacuum.c b/contrib/bloom/blvacuum.c
new file mode 100644
index ...fb8d9b8
*** a/contrib/bloom/blvacuum.c
--- b/contrib/bloom/blvacuum.c
***************
*** 0 ****
--- 1,212 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blvacuum.c
+  *		Bloom VACUUM functions.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blvacuum.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "catalog/storage.h"
+ #include "commands/vacuum.h"
+ #include "miscadmin.h"
+ #include "postmaster/autovacuum.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "storage/lmgr.h"
+ 
+ #include "bloom.h"
+ 
+ /*
+  * Bulk deletion of all index entries pointing to a set of heap tuples.
+  * The set of target tuples is specified via a callback routine that tells
+  * whether any given heap tuple (identified by ItemPointer) is being deleted.
+  *
+  * Result: a palloc'd struct containing statistical info for VACUUM displays.
+  */
+ IndexBulkDeleteResult *
+ blbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
+ 			 IndexBulkDeleteCallback callback, void *callback_state)
+ {
+ 	Relation	index = info->index;
+ 	BlockNumber blkno,
+ 				npages;
+ 	FreeBlockNumberArray notFullPage;
+ 	int			countPage = 0;
+ 	BloomState	state;
+ 	Buffer		buffer;
+ 	Page		page;
+ 	GenericXLogState *gxlogState;
+ 
+ 	if (stats == NULL)
+ 		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+ 
+ 	initBloomState(&state, index);
+ 
+ 	/*
+ 	 * Interate over the pages. We don't care about concurrently added pages,
+ 	 * they can't contain tuples to delete.
+ 	 */
+ 	npages = RelationGetNumberOfBlocks(index);
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		BloomTuple *itup,
+ 				   *itupPtr,
+ 				   *itupEnd;
+ 
+ 		buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
+ 									RBM_NORMAL, info->strategy);
+ 
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		gxlogState = GenericXLogStart(index);
+ 		page = GenericXLogRegister(gxlogState, buffer, false);
+ 
+ 		if (BloomPageIsDeleted(page))
+ 		{
+ 			UnlockReleaseBuffer(buffer);
+ 			CHECK_FOR_INTERRUPTS();
+ 			continue;
+ 		}
+ 
+ 		/* Iterate over the tuples */
+ 		itup = BloomPageGetTuple(&state, page, 1);
+ 		itupPtr = BloomPageGetTuple(&state, page, 1);
+ 		itupEnd = BloomPageGetTuple(&state, page, BloomPageGetMaxOffset(page) + 1);
+ 		while (itup < itupEnd)
+ 		{
+ 			/* Do we have to delete this tuple? */
+ 			if (callback(&itup->heapPtr, callback_state))
+ 			{
+ 				stats->tuples_removed += 1;
+ 				BloomPageGetOpaque(page)->maxoff--;
+ 			}
+ 			else
+ 			{
+ 				if (itupPtr != itup)
+ 				{
+ 					/*
+ 					 * If we already delete something before, we have to move
+ 					 * this tuple backward.
+ 					 */
+ 					memmove((Pointer) itupPtr, (Pointer) itup,
+ 							state.sizeOfBloomTuple);
+ 				}
+ 				stats->num_index_tuples++;
+ 				itupPtr = BloomPageGetNextTuple(&state, itupPtr);
+ 			}
+ 
+ 			itup = BloomPageGetNextTuple(&state, itup);
+ 		}
+ 
+ 		Assert(itupPtr == BloomPageGetTuple(&state, page, BloomPageGetMaxOffset(page) + 1));
+ 
+ 		if (!BloomPageIsDeleted(page) &&
+ 			BloomPageGetFreeSpace(&state, page) > state.sizeOfBloomTuple &&
+ 			countPage < BloomMetaBlockN)
+ 			notFullPage[countPage++] = blkno;
+ 
+ 		/* Did we delete something? */
+ 		if (itupPtr != itup)
+ 		{
+ 			/* Is it empty page now? */
+ 			if (itupPtr == BloomPageGetData(page))
+ 				BloomPageSetDeleted(page);
+ 			/* Adjust pg_lower */
+ 			((PageHeader) page)->pd_lower = (Pointer) itupPtr - page;
+ 			/* Finish WAL-logging */
+ 			GenericXLogFinish(gxlogState);
+ 		}
+ 		else
+ 		{
+ 			/* Didn't change anything: abort WAL-logging */
+ 			GenericXLogAbort(gxlogState);
+ 		}
+ 		UnlockReleaseBuffer(buffer);
+ 		CHECK_FOR_INTERRUPTS();
+ 	}
+ 
+ 	if (countPage > 0)
+ 	{
+ 		BloomMetaPageData *metaData;
+ 
+ 		buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 		gxlogState = GenericXLogStart(index);
+ 		page = GenericXLogRegister(gxlogState, buffer, false);
+ 
+ 		metaData = BloomPageGetMeta(page);
+ 		memcpy(metaData->notFullPage, notFullPage, sizeof(FreeBlockNumberArray));
+ 		metaData->nStart = 0;
+ 		metaData->nEnd = countPage;
+ 
+ 		GenericXLogFinish(gxlogState);
+ 		UnlockReleaseBuffer(buffer);
+ 	}
+ 
+ 	return stats;
+ }
+ 
+ /*
+  * Post-VACUUM cleanup.
+  *
+  * Result: a palloc'd struct containing statistical info for VACUUM displays.
+  */
+ IndexBulkDeleteResult *
+ blvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
+ {
+ 	Relation	index = info->index;
+ 	BlockNumber npages,
+ 				blkno;
+ 	BlockNumber totFreePages;
+ 
+ 	if (info->analyze_only)
+ 		return stats;
+ 
+ 	if (stats == NULL)
+ 		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+ 
+ 	/*
+ 	 * Iterate over the pages: insert deleted pages into FSM and collect
+ 	 * statistics.
+ 	 */
+ 	npages = RelationGetNumberOfBlocks(index);
+ 	totFreePages = 0;
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		Buffer		buffer;
+ 		Page		page;
+ 
+ 		vacuum_delay_point();
+ 
+ 		buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
+ 									RBM_NORMAL, info->strategy);
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 		page = (Page) BufferGetPage(buffer);
+ 
+ 		if (BloomPageIsDeleted(page))
+ 		{
+ 			RecordFreeIndexPage(index, blkno);
+ 			totFreePages++;
+ 		}
+ 		else
+ 		{
+ 			stats->num_index_tuples += BloomPageGetMaxOffset(page);
+ 			stats->estimated_count += BloomPageGetMaxOffset(page);
+ 		}
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 	}
+ 
+ 	IndexFreeSpaceMapVacuum(info->index);
+ 	stats->pages_free = totFreePages;
+ 	stats->num_pages = RelationGetNumberOfBlocks(index);
+ 
+ 	return stats;
+ }
diff --git a/contrib/bloom/blvalidate.c b/contrib/bloom/blvalidate.c
new file mode 100644
index ...12e7c7d
*** a/contrib/bloom/blvalidate.c
--- b/contrib/bloom/blvalidate.c
***************
*** 0 ****
--- 1,220 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blvalidate.c
+  *	  Opclass validator for bloom.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blvalidate.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/amvalidate.h"
+ #include "access/htup_details.h"
+ #include "catalog/pg_amop.h"
+ #include "catalog/pg_amproc.h"
+ #include "catalog/pg_opclass.h"
+ #include "catalog/pg_opfamily.h"
+ #include "catalog/pg_type.h"
+ #include "utils/builtins.h"
+ #include "utils/lsyscache.h"
+ #include "utils/syscache.h"
+ 
+ #include "bloom.h"
+ 
+ /*
+  * Validator for a bloom opclass.
+  */
+ bool
+ blvalidate(Oid opclassoid)
+ {
+ 	bool		result = true;
+ 	HeapTuple	classtup;
+ 	Form_pg_opclass classform;
+ 	Oid			opfamilyoid;
+ 	Oid			opcintype;
+ 	Oid			opckeytype;
+ 	char	   *opclassname;
+ 	HeapTuple	familytup;
+ 	Form_pg_opfamily familyform;
+ 	char	   *opfamilyname;
+ 	CatCList   *proclist,
+ 			   *oprlist;
+ 	List	   *grouplist;
+ 	OpFamilyOpFuncGroup *opclassgroup;
+ 	int			i;
+ 	ListCell   *lc;
+ 
+ 	/* Fetch opclass information */
+ 	classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
+ 	if (!HeapTupleIsValid(classtup))
+ 		elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
+ 	classform = (Form_pg_opclass) GETSTRUCT(classtup);
+ 
+ 	opfamilyoid = classform->opcfamily;
+ 	opcintype = classform->opcintype;
+ 	opckeytype = classform->opckeytype;
+ 	if (!OidIsValid(opckeytype))
+ 		opckeytype = opcintype;
+ 	opclassname = NameStr(classform->opcname);
+ 
+ 	/* Fetch opfamily information */
+ 	familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
+ 	if (!HeapTupleIsValid(familytup))
+ 		elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
+ 	familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
+ 
+ 	opfamilyname = NameStr(familyform->opfname);
+ 
+ 	/* Fetch all operators and support functions of the opfamily */
+ 	oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
+ 	proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
+ 
+ 	/* Check individual support functions */
+ 	for (i = 0; i < proclist->n_members; i++)
+ 	{
+ 		HeapTuple	proctup = &proclist->members[i]->tuple;
+ 		Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
+ 		bool		ok;
+ 
+ 		/*
+ 		 * All bloom support functions should be registered with matching
+ 		 * left/right types
+ 		 */
+ 		if (procform->amproclefttype != procform->amprocrighttype)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains support procedure %s with cross-type registration",
+ 							opfamilyname,
+ 							format_procedure(procform->amproc))));
+ 			result = false;
+ 		}
+ 
+ 		/*
+ 		 * We can't check signatures except within the specific opclass, since
+ 		 * we need to know the associated opckeytype in many cases.
+ 		 */
+ 		if (procform->amproclefttype != opcintype)
+ 			continue;
+ 
+ 		/* Check procedure numbers and function signatures */
+ 		switch (procform->amprocnum)
+ 		{
+ 			case BLOOM_HASH_PROC:
+ 				ok = check_amproc_signature(procform->amproc, INT4OID, false,
+ 											1, 1, opckeytype);
+ 				break;
+ 			default:
+ 				ereport(INFO,
+ 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 						 errmsg("bloom opfamily %s contains function %s with invalid support number %d",
+ 								opfamilyname,
+ 								format_procedure(procform->amproc),
+ 								procform->amprocnum)));
+ 				result = false;
+ 				continue;		/* don't want additional message */
+ 		}
+ 
+ 		if (!ok)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("gist opfamily %s contains function %s with wrong signature for support number %d",
+ 							opfamilyname,
+ 							format_procedure(procform->amproc),
+ 							procform->amprocnum)));
+ 			result = false;
+ 		}
+ 	}
+ 
+ 	/* Check individual operators */
+ 	for (i = 0; i < oprlist->n_members; i++)
+ 	{
+ 		HeapTuple	oprtup = &oprlist->members[i]->tuple;
+ 		Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
+ 
+ 		/* Check it's allowed strategy for bloom */
+ 		if (oprform->amopstrategy < 1 ||
+ 			oprform->amopstrategy > BLOOM_NSTRATEGIES)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains operator %s with invalid strategy number %d",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr),
+ 							oprform->amopstrategy)));
+ 			result = false;
+ 		}
+ 
+ 		/* bloom doesn't support ORDER BY operators */
+ 		if (oprform->amoppurpose != AMOP_SEARCH ||
+ 			OidIsValid(oprform->amopsortfamily))
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains invalid ORDER BY specification for operator %s",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr))));
+ 			result = false;
+ 		}
+ 
+ 		/* Check operator signature --- same for all bloom strategies */
+ 		if (!check_amop_signature(oprform->amopopr, BOOLOID,
+ 								  oprform->amoplefttype,
+ 								  oprform->amoprighttype))
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains operator %s with wrong signature",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr))));
+ 			result = false;
+ 		}
+ 	}
+ 
+ 	/* Now check for inconsistent groups of operators/functions */
+ 	grouplist = identify_opfamily_groups(oprlist, proclist);
+ 	opclassgroup = NULL;
+ 	foreach(lc, grouplist)
+ 	{
+ 		OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
+ 
+ 		/* Remember the group exactly matching the test opclass */
+ 		if (thisgroup->lefttype == opcintype &&
+ 			thisgroup->righttype == opcintype)
+ 			opclassgroup = thisgroup;
+ 
+ 		/*
+ 		 * There is not a lot we can do to check the operator sets, since each
+ 		 * bloom opclass is more or less a law unto itself, and some contain
+ 		 * only operators that are binary-compatible with the opclass datatype
+ 		 * (meaning that empty operator sets can be OK).  That case also means
+ 		 * that we shouldn't insist on nonempty function sets except for the
+ 		 * opclass's own group.
+ 		 */
+ 	}
+ 
+ 	/* Check that the originally-named opclass is complete */
+ 	for (i = 1; i <= BLOOM_NPROC; i++)
+ 	{
+ 		if (opclassgroup &&
+ 			(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
+ 			continue;			/* got it */
+ 		ereport(INFO,
+ 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 				 errmsg("bloom opclass %s is missing support function %d",
+ 						opclassname, i)));
+ 		result = false;
+ 	}
+ 
+ 	ReleaseCatCacheList(proclist);
+ 	ReleaseCatCacheList(oprlist);
+ 	ReleaseSysCache(familytup);
+ 	ReleaseSysCache(classtup);
+ 
+ 	return result;
+ }
diff --git a/contrib/bloom/expected/bloom.out b/contrib/bloom/expected/bloom.out
new file mode 100644
index ...5e8269f
*** a/contrib/bloom/expected/bloom.out
--- b/contrib/bloom/expected/bloom.out
***************
*** 0 ****
--- 1,122 ----
+ CREATE EXTENSION bloom;
+ CREATE TABLE tst (
+ 	i	int4,
+ 	t	text
+ );
+ INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,100000) i;
+ CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+ SET enable_seqscan=on;
+ SET enable_bitmapscan=off;
+ SET enable_indexscan=off;
+ SELECT count(*) FROM tst WHERE i = 7;
+  count 
+ -------
+  10000
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   6264
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
+  count 
+ -------
+    588
+ (1 row)
+ 
+ SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 7;
+                 QUERY PLAN                 
+ -------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: (i = 7)
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: (i = 7)
+ (5 rows)
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE t = '5';
+                 QUERY PLAN                 
+ -------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: (t = '5'::text)
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: (t = '5'::text)
+ (5 rows)
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
+                        QUERY PLAN                        
+ ---------------------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: ((i = 7) AND (t = '5'::text))
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: ((i = 7) AND (t = '5'::text))
+ (5 rows)
+ 
+ SELECT count(*) FROM tst WHERE i = 7;
+  count 
+ -------
+  10000
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   6264
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
+  count 
+ -------
+    588
+ (1 row)
+ 
+ DELETE FROM tst;
+ INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,100000) i;
+ VACUUM ANALYZE tst;
+ SELECT count(*) FROM tst WHERE i = 7;
+  count 
+ -------
+  10000
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   6264
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
+  count 
+ -------
+    588
+ (1 row)
+ 
+ VACUUM FULL tst;
+ SELECT count(*) FROM tst WHERE i = 7;
+  count 
+ -------
+  10000
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   6264
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
+  count 
+ -------
+    588
+ (1 row)
+ 
+ RESET enable_seqscan;
+ RESET enable_bitmapscan;
+ RESET enable_indexscan;
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
new file mode 100644
index ...f9d0ad4
*** a/contrib/bloom/sql/bloom.sql
--- b/contrib/bloom/sql/bloom.sql
***************
*** 0 ****
--- 1,47 ----
+ CREATE EXTENSION bloom;
+ 
+ CREATE TABLE tst (
+ 	i	int4,
+ 	t	text
+ );
+ 
+ INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,100000) i;
+ CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+ 
+ SET enable_seqscan=on;
+ SET enable_bitmapscan=off;
+ SET enable_indexscan=off;
+ 
+ SELECT count(*) FROM tst WHERE i = 7;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
+ 
+ SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 7;
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE t = '5';
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
+ 
+ SELECT count(*) FROM tst WHERE i = 7;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
+ 
+ DELETE FROM tst;
+ INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,100000) i;
+ VACUUM ANALYZE tst;
+ 
+ SELECT count(*) FROM tst WHERE i = 7;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
+ 
+ VACUUM FULL tst;
+ 
+ SELECT count(*) FROM tst WHERE i = 7;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
+ 
+ RESET enable_seqscan;
+ RESET enable_bitmapscan;
+ RESET enable_indexscan;
diff --git a/contrib/bloom/t/001_wal.pl b/contrib/bloom/t/001_wal.pl
new file mode 100644
index ...bbb398b
*** a/contrib/bloom/t/001_wal.pl
--- b/contrib/bloom/t/001_wal.pl
***************
*** 0 ****
--- 1,75 ----
+ # Test generic xlog record work for bloom index replication.
+ use strict;
+ use warnings;
+ use PostgresNode;
+ use TestLib;
+ use Test::More tests => 31;
+ 
+ my $node_master;
+ my $node_standby;
+ 
+ # Run few queries on both master and standby and check their results match.
+ sub test_index_replay
+ {
+ 	my ($test_name) = @_;
+ 
+ 	# Wait for standby to catch up
+ 	my $applname = $node_standby->name;
+ 	my $caughtup_query =
+ 		"SELECT pg_current_xlog_location() <= write_location FROM pg_stat_replication WHERE application_name = '$applname';";
+ 	$node_master->poll_query_until('postgres', $caughtup_query)
+ 	  or die "Timed out while waiting for standby 1 to catch up";
+ 
+ 	my $queries = qq(SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ SELECT * FROM tst WHERE i = 0;
+ SELECT * FROM tst WHERE i = 3;
+ SELECT * FROM tst WHERE t = 'b';
+ SELECT * FROM tst WHERE t = 'f';
+ SELECT * FROM tst WHERE i = 3 AND t = 'c';
+ SELECT * FROM tst WHERE i = 7 AND t = 'e';
+ );
+ 
+ 	# Run test queries and compare their result
+ 	my $master_result = $node_master->psql("postgres", $queries);
+ 	my $standby_result = $node_standby->psql("postgres", $queries);
+ 
+ 	is($master_result, $standby_result, "$test_name: query result matches");
+ }
+ 
+ # Initialize master node
+ $node_master = get_new_node('master');
+ $node_master->init(allows_streaming => 1);
+ $node_master->start;
+ my $backup_name = 'my_backup';
+ 
+ # Take backup
+ $node_master->backup($backup_name);
+ 
+ # Create streaming standby linking to master
+ $node_standby = get_new_node('standby');
+ $node_standby->init_from_backup($node_master, $backup_name,
+ 	has_streaming => 1);
+ $node_standby->start;
+ 
+ # Create some bloom index on master
+ $node_master->psql("postgres", "CREATE EXTENSION bloom;");
+ $node_master->psql("postgres", "CREATE TABLE tst (i int4, t text);");
+ $node_master->psql("postgres", "INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,100000) i;");
+ $node_master->psql("postgres", "CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);");
+ 
+ # Test that queries give same result
+ test_index_replay('initial');
+ 
+ # Run 10 cycles of table modification. Run test queries after each modification.
+ for (my $i = 1; $i <= 10; $i++)
+ {
+ 	$node_master->psql("postgres", "DELETE FROM tst WHERE i = $i;");
+ 	test_index_replay("delete $i");
+ 	$node_master->psql("postgres", "VACUUM tst;");
+ 	test_index_replay("vacuum $i");
+ 	my ($start, $end) = (100001 + ($i - 1) * 10000, 100000 + $i * 10000);
+ 	$node_master->psql("postgres", "INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series($start,$end) i;");
+ 	test_index_replay("insert $i");
+ }
diff --git a/doc/src/sgml/bloom.sgml b/doc/src/sgml/bloom.sgml
new file mode 100644
index ...c207e6d
*** a/doc/src/sgml/bloom.sgml
--- b/doc/src/sgml/bloom.sgml
***************
*** 0 ****
--- 1,218 ----
+ <!-- doc/src/sgml/bloom.sgml -->
+ 
+ <sect1 id="bloom" xreflabel="bloom">
+  <title>bloom</title>
+ 
+  <indexterm zone="bloom">
+   <primary>bloom</primary>
+  </indexterm>
+ 
+  <para>
+   <literal>bloom</> is a contrib which implements index access method.  It comes
+   as example of custom access methods and generic WAL records usage.  But it
+   is also useful itself.
+  </para>
+ 
+  <sect2>
+   <title>Introduction</title>
+ 
+   <para>
+    Implementation of
+    <ulink url="http://en.wikipedia.org/wiki/Bloom_filter">Bloom filter</ulink>
+    allows fast exclusion of non-candidate tuples.
+    Since signature is a lossy representation of all indexed attributes, 
+    search results should be rechecked using heap information. 
+    User can specify signature length (in uint16, default is 5) and the number of 
+    bits, which can be setted, per attribute (1 < colN < 2048).
+   </para>
+ 
+   <para>
+    This index is useful if table has many attributes and queries can include
+    their arbitary combinations.  Traditional <literal>btree</> index is faster
+    than bloom index, but it'd require too many indexes to support all possible 
+    queries, while one need only one bloom index.  Bloom index supports only 
+    equality comparison.  Since it's a signature file, not a tree, it always
+    should be readed fully, but sequentially, so index search performance is 
+    constant and doesn't depend on a query. 
+   </para>
+  </sect2>
+ 
+  <sect2>
+   <title>Parameters</title>
+ 
+   <para>
+    <literal>bloom</> indexes accept following parameters in <literal>WITH</>
+    clause.
+   </para>
+ 
+    <variablelist>
+    <varlistentry>
+     <term><literal>length</></term>
+     <listitem>
+      <para>
+       Length of signature in uint16 type values
+      </para>
+     </listitem>
+    </varlistentry>
+    </variablelist>
+    <variablelist>
+    <varlistentry>
+     <term><literal>col1 &mdash; col16</></term>
+     <listitem>
+      <para>
+       Number of bits for corresponding column
+      </para>
+     </listitem>
+    </varlistentry>
+    </variablelist>
+  </sect2>
+ 
+  <sect2>
+   <title>Examples</title>
+ 
+   <para>
+    Example of index definition is given below.
+   </para>
+ 
+ <programlisting>
+ CREATE INDEX bloomidx ON tbloom(i1,i2,i3) 
+        WITH (length=5, col1=2, col2=2, col3=4);
+ </programlisting>
+ 
+   <para>
+    Here, we create bloom index with signature length 80 bits and attributes
+    i1, i2  mapped to 2 bits, attribute i3 - to 4 bits.
+   </para>
+ 
+   <para>
+    Example of index definition and usage is given below.
+   </para>
+ 
+ <programlisting>
+ CREATE TABLE tbloom AS
+ SELECT
+     random()::int as i1,
+     random()::int as i2,
+     random()::int as i3,
+     random()::int as i4,
+     random()::int as i5,
+     random()::int as i6,
+     random()::int as i7,
+     random()::int as i8,
+     random()::int as i9,
+     random()::int as i10,
+     random()::int as i11,
+     random()::int as i12,
+     random()::int as i13
+ FROM
+     generate_series(1,1000);
+ CREATE INDEX bloomidx ON tbloom USING
+              bloom (i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12);
+ SELECT pg_relation_size('bloomidx');
+ CREATE index btree_idx ON tbloom(i1,i2,i3,i4,i5,i6,i7,i8,i9,i10,i11,i12);
+ SELECT pg_relation_size('btree_idx');
+ </programlisting>
+ 
+ <programlisting>
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                                    QUERY PLAN
+ -----------------------------------------------------------------------------------------------------------------
+  Bitmap Heap Scan on tbloom  (cost=1.50..5.52 rows=1 width=52) (actual time=0.057..0.057 rows=0 loops=1)
+    Recheck Cond: ((i2 = 20) AND (i10 = 15))
+    ->  Bitmap Index Scan on bloomidx  (cost=0.00..1.50 rows=1 width=0) (actual time=0.041..0.041 rows=9 loops=1)
+          Index Cond: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.081 ms
+ (5 rows)
+ </programlisting>
+ 
+   <para>
+    Seqscan is slow.
+   </para>
+ 
+ <programlisting>
+ =# SET enable_bitmapscan = off;
+ =# SET enable_indexscan = off;
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                             QUERY PLAN
+ --------------------------------------------------------------------------------------------------
+  Seq Scan on tbloom  (cost=0.00..25.00 rows=1 width=52) (actual time=0.162..0.162 rows=0 loops=1)
+    Filter: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.181 ms
+ (3 rows)
+ </programlisting>
+ 
+  <para>
+   Btree index will be not used for this query.
+  </para>
+ 
+ <programlisting>
+ =# DROP INDEX bloomidx;
+ =# CREATE INDEX btree_idx ON tbloom(i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12);
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                             QUERY PLAN
+ --------------------------------------------------------------------------------------------------
+  Seq Scan on tbloom (cost=0.00..25.00 rows=1 width=52) (actual time=0.210..0.210 rows=0 loops=1)
+    Filter: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.250 ms
+ (3 rows)
+ </programlisting>
+  </sect2>
+ 
+  <sect2>
+   <title>Opclass interface</title>
+ 
+   <para>
+    Bloom opclass interface is simple.  It requires 1 supporting function:
+    hash function for indexing datatype.  And it provides 1 search operator:
+    equality operator.  The example below shows <literal>opclass</> definition
+    for <literal>text</> datatype.
+   </para>
+ 
+ <programlisting>
+ CREATE OPERATOR CLASS text_ops
+ DEFAULT FOR TYPE text USING bloom AS
+     OPERATOR    1   =(text, text),
+     FUNCTION    1   hashtext(text);
+ </programlisting>
+  </sect2>
+ 
+  <sect2>
+   <title>Limitation</title>
+   <para>
+ 
+    <itemizedlist>
+     <listitem>
+      <para>
+       For now, only opclasses for <literal>int4</>, <literal>text</> comes
+       with contrib.  However, users may define more of them.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       Only <literal>=</literal> operator is supported for search now.  But it's
+       possible to add support of arrays with contains and intersection
+       operations in future.
+      </para>
+     </listitem>
+    </itemizedlist>
+   </para>
+  </sect2>
+ 
+  <sect2>
+   <title>Authors</title>
+ 
+   <para>
+    Teodor Sigaev <email>teodor@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+ 
+   <para>
+    Alexander Korotkov <email>a.korotkov@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+ 
+   <para>
+    Oleg Bartunov <email>obartunov@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+  </sect2>
+ 
+ </sect1>
diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
new file mode 100644
index 4e3f337..c8708ec
*** a/doc/src/sgml/contrib.sgml
--- b/doc/src/sgml/contrib.sgml
*************** CREATE EXTENSION <replaceable>module_nam
*** 105,110 ****
--- 105,111 ----
   &adminpack;
   &auth-delay;
   &auto-explain;
+  &bloom;
   &btree-gin;
   &btree-gist;
   &chkpass;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
new file mode 100644
index 9046f50..6c0ad3f
*** a/doc/src/sgml/filelist.sgml
--- b/doc/src/sgml/filelist.sgml
***************
*** 107,112 ****
--- 107,113 ----
  <!ENTITY adminpack       SYSTEM "adminpack.sgml">
  <!ENTITY auth-delay      SYSTEM "auth-delay.sgml">
  <!ENTITY auto-explain    SYSTEM "auto-explain.sgml">
+ <!ENTITY bloom           SYSTEM "bloom.sgml">
  <!ENTITY btree-gin       SYSTEM "btree-gin.sgml">
  <!ENTITY btree-gist      SYSTEM "btree-gist.sgml">
  <!ENTITY chkpass         SYSTEM "chkpass.sgml">
#93Markus Nullmeier
dq124@uni-heidelberg.de
In reply to: Alexander Korotkov (#92)
1 attachment(s)
Re: WIP: Access method extendability

On 03/31/16 17:29, Alexander Korotkov wrote:

New revision of patches is attached.

1) API of generic xlog was changed: now there is no static variables,
GenericXLogStart() returns palloc'd struct.

Attached are two trivial documentation editing fixes for this, as an
incremental patch.

--
Markus Nullmeier http://www.g-vo.org
German Astrophysical Virtual Observatory (GAVO)

Attachments:

generic-xlog-15-fixes-delta.patchtext/x-patch; name=generic-xlog-15-fixes-delta.patchDownload
diff --git a/doc/src/sgml/generic-wal.sgml b/doc/src/sgml/generic-wal.sgml
index 6655f22..b3388ba 100644
--- a/doc/src/sgml/generic-wal.sgml
+++ b/doc/src/sgml/generic-wal.sgml
@@ -43,7 +43,7 @@
 
     <listitem>
      <para>
-      <function>GenericXLogAbort(state)</> &mdash; finish construction of
+      <function>GenericXLogFinish(state)</> &mdash; finish construction of
       a generic xlog record.
      </para>
     </listitem>
@@ -52,7 +52,7 @@
 
   <para>
    The xlog record construction can be canceled between any of the above
-   steps by calling <function>GenericXLogAbort()</>.  This will discard all
+   steps by calling <function>GenericXLogAbort(state)</>.  This will discard all
    changes to the page image copies.
   </para>
 
#94Aleksander Alekseev
a.alekseev@postgrespro.ru
In reply to: Alexander Korotkov (#92)
Re: WIP: Access method extendability

Hello, Alexander

Hi!

New revision of patches is attached.

Code looks much better now, thanks. Still I believe it could be improved.

I don't think that using srand() / rand() in signValue procedure the
way you did is such a good idea. You create a side affect (changing
current randseed) which could cause problems in some cases. And there
is no real need for that. For instance you could use following formula
instead:

hash(attno || hashVal || j)

And a few more things.

+ 	memset(opaque, 0, sizeof(BloomPageOpaqueData));
+ 	opaque->maxoff = 0;

This looks a bit redundant.

+ for (my $i = 1; $i <= 10; $i++)

More idiomatic Perl would be `for my $i (1..10)`.

+ 			UnlockReleaseBuffer(buffer);
+ 			ReleaseBuffer(metaBuffer);
+ 			goto away;

In general I don't have anything against goto. But are you sure that
using it here is really justified?

--
Best regards,
Aleksander Alekseev
http://eax.me/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#95Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Aleksander Alekseev (#94)
1 attachment(s)
Re: WIP: Access method extendability

Hi!

On Fri, Apr 1, 2016 at 12:45 PM, Aleksander Alekseev <
a.alekseev@postgrespro.ru> wrote:

Code looks much better now, thanks. Still I believe it could be improved.

I don't think that using srand() / rand() in signValue procedure the
way you did is such a good idea. You create a side affect (changing
current randseed) which could cause problems in some cases. And there
is no real need for that. For instance you could use following formula
instead:

hash(attno || hashVal || j)

I've discussed this with Teodor privately. Extra hash calculation could
cause performance regression. He proposed to use own random generator
instead. Implemented in attached version of patch.

And a few more things.

+     memset(opaque, 0, sizeof(BloomPageOpaqueData));
+     opaque->maxoff = 0;

This looks a bit redundant.

Fixed.

+ for (my $i = 1; $i <= 10; $i++)

More idiomatic Perl would be `for my $i (1..10)`.

Fixed.

+                     UnlockReleaseBuffer(buffer);
+                     ReleaseBuffer(metaBuffer);
+                     goto away;

In general I don't have anything against goto. But are you sure that
using it here is really justified?

Fixed with small code duplication which seems to be better than goto.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

Attachments:

0003-bloom-contrib.16.patchapplication/octet-stream; name=0003-bloom-contrib.16.patchDownload
diff --git a/contrib/Makefile b/contrib/Makefile
new file mode 100644
index d12dd63..25263c0
*** a/contrib/Makefile
--- b/contrib/Makefile
*************** SUBDIRS = \
*** 8,13 ****
--- 8,14 ----
  		adminpack	\
  		auth_delay	\
  		auto_explain	\
+ 		bloom		\
  		btree_gin	\
  		btree_gist	\
  		chkpass		\
diff --git a/contrib/bloom/.gitignore b/contrib/bloom/.gitignore
new file mode 100644
index ...5dcb3ff
*** a/contrib/bloom/.gitignore
--- b/contrib/bloom/.gitignore
***************
*** 0 ****
--- 1,4 ----
+ # Generated subdirectories
+ /log/
+ /results/
+ /tmp_check/
diff --git a/contrib/bloom/Makefile b/contrib/bloom/Makefile
new file mode 100644
index ...13bd397
*** a/contrib/bloom/Makefile
--- b/contrib/bloom/Makefile
***************
*** 0 ****
--- 1,24 ----
+ # contrib/bloom/Makefile
+ 
+ MODULE_big = bloom
+ OBJS = blcost.o blinsert.o blscan.o blutils.o blvacuum.o blvalidate.o $(WIN32RES)
+ 
+ EXTENSION = bloom
+ DATA = bloom--1.0.sql
+ PGFILEDESC = "bloom access method - signature file based index"
+ 
+ REGRESS = bloom
+ 
+ ifdef USE_PGXS
+ PG_CONFIG = pg_config
+ PGXS := $(shell $(PG_CONFIG) --pgxs)
+ include $(PGXS)
+ else
+ subdir = contrib/bloom
+ top_builddir = ../..
+ include $(top_builddir)/src/Makefile.global
+ include $(top_srcdir)/contrib/contrib-global.mk
+ endif
+ 
+ wal-check: temp-install
+ 	$(prove_check)
diff --git a/contrib/bloom/blcost.c b/contrib/bloom/blcost.c
new file mode 100644
index ...9897898
*** a/contrib/bloom/blcost.c
--- b/contrib/bloom/blcost.c
***************
*** 0 ****
--- 1,48 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blcost.c
+  *		Cost estimate function for bloom indexes.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blcost.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "fmgr.h"
+ #include "optimizer/cost.h"
+ #include "utils/selfuncs.h"
+ 
+ #include "bloom.h"
+ 
+ /*
+  * Estimate cost of bloom index scan.
+  */
+ void
+ blcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
+ 			   Cost *indexStartupCost, Cost *indexTotalCost,
+ 			   Selectivity *indexSelectivity, double *indexCorrelation)
+ {
+ 	IndexOptInfo *index = path->indexinfo;
+ 	List	   *qinfos;
+ 	GenericCosts costs;
+ 
+ 	/* Do preliminary analysis of indexquals */
+ 	qinfos = deconstruct_indexquals(path);
+ 
+ 	MemSet(&costs, 0, sizeof(costs));
+ 
+ 	/* We have to visit all index tuples anyway */
+ 	costs.numIndexTuples = index->tuples;
+ 
+ 	/* Use generic estimate */
+ 	genericcostestimate(root, path, loop_count, qinfos, &costs);
+ 
+ 	*indexStartupCost = costs.indexStartupCost;
+ 	*indexTotalCost = costs.indexTotalCost;
+ 	*indexSelectivity = costs.indexSelectivity;
+ 	*indexCorrelation = costs.indexCorrelation;
+ }
diff --git a/contrib/bloom/blinsert.c b/contrib/bloom/blinsert.c
new file mode 100644
index ...9e66780
*** a/contrib/bloom/blinsert.c
--- b/contrib/bloom/blinsert.c
***************
*** 0 ****
--- 1,313 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blinsert.c
+  *		Bloom index build and insert functions.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blinsert.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "access/generic_xlog.h"
+ #include "catalog/index.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ 
+ #include "bloom.h"
+ 
+ PG_MODULE_MAGIC;
+ 
+ /*
+  * State of bloom index build.  We accumulate one page data here before
+  * flushing it to buffer manager.
+  */
+ typedef struct
+ {
+ 	BloomState	blstate;		/* bloom index state */
+ 	MemoryContext tmpCtx;		/* temporary memory context reset after
+ 								 * each tuple */
+ 	char		data[BLCKSZ];	/* cached page */
+ 	int64		count;			/* number of tuples in cached page */
+ }	BloomBuildState;
+ 
+ /*
+  * Flush page cached in BloomBuildState.
+  */
+ static void
+ flushCachedPage(Relation index, BloomBuildState *buildstate)
+ {
+ 	Page		page;
+ 	Buffer		buffer = BloomNewBuffer(index);
+ 	GenericXLogState *state;
+ 
+ 	state = GenericXLogStart(index);
+ 	page = GenericXLogRegister(state, buffer, true);
+ 	memcpy(page, buildstate->data, BLCKSZ);
+ 	GenericXLogFinish(state);
+ 	UnlockReleaseBuffer(buffer);
+ }
+ 
+ /*
+  * (Re)initialize cached page in BloomBuildState.
+  */
+ static void
+ initCachedPage(BloomBuildState *buildstate)
+ {
+ 	memset(buildstate->data, 0, BLCKSZ);
+ 	BloomInitPage(buildstate->data, 0);
+ 	buildstate->count = 0;
+ }
+ 
+ /*
+  * Per-tuple callback from IndexBuildHeapScan.
+  */
+ static void
+ bloomBuildCallback(Relation index, HeapTuple htup, Datum *values,
+ 				   bool *isnull, bool tupleIsAlive, void *state)
+ {
+ 	BloomBuildState *buildstate = (BloomBuildState *) state;
+ 	MemoryContext oldCtx;
+ 	BloomTuple *itup;
+ 
+ 	oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
+ 
+ 	itup = BloomFormTuple(&buildstate->blstate, &htup->t_self, values, isnull);
+ 
+ 	/* Try to add next item to cached page */
+ 	if (BloomPageAddItem(&buildstate->blstate, buildstate->data, itup))
+ 	{
+ 		/* Next item was added successfully */
+ 		buildstate->count++;
+ 	}
+ 	else
+ 	{
+ 		/* Cached page is full, flush it out and make a new one */
+ 		flushCachedPage(index, buildstate);
+ 
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		initCachedPage(buildstate);
+ 
+ 		if (BloomPageAddItem(&buildstate->blstate, buildstate->data, itup) == false)
+ 		{
+ 			/* We shouldn't be here since we're inserting to the empty page */
+ 			elog(ERROR, "can not add new tuple");
+ 		}
+ 	}
+ 
+ 	MemoryContextSwitchTo(oldCtx);
+ 	MemoryContextReset(buildstate->tmpCtx);
+ }
+ 
+ /*
+  * Build a new bloom index.
+  */
+ IndexBuildResult *
+ blbuild(Relation heap, Relation index, IndexInfo *indexInfo)
+ {
+ 	IndexBuildResult *result;
+ 	double		reltuples;
+ 	BloomBuildState buildstate;
+ 
+ 	if (RelationGetNumberOfBlocks(index) != 0)
+ 		elog(ERROR, "index \"%s\" already contains data",
+ 			 RelationGetRelationName(index));
+ 
+ 	/* Initialize the meta page */
+ 	BloomInitMetapage(index);
+ 
+ 	/* Initialize the bloom build state */
+ 	memset(&buildstate, 0, sizeof(buildstate));
+ 	initBloomState(&buildstate.blstate, index);
+ 	buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
+ 											  "Bloom build temporary context",
+ 											  ALLOCSET_DEFAULT_MINSIZE,
+ 											  ALLOCSET_DEFAULT_INITSIZE,
+ 											  ALLOCSET_DEFAULT_MAXSIZE);
+ 	initCachedPage(&buildstate);
+ 
+ 	/* Do the heap scan */
+ 	reltuples = IndexBuildHeapScan(heap, index, indexInfo, true,
+ 								   bloomBuildCallback, (void *) &buildstate);
+ 
+ 	/*
+ 	 * There are could be some items in cached page.  Flush this page
+ 	 * if needed.
+ 	 */
+ 	if (buildstate.count > 0)
+ 		flushCachedPage(index, &buildstate);
+ 
+ 	MemoryContextDelete(buildstate.tmpCtx);
+ 
+ 	result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
+ 	result->heap_tuples = result->index_tuples = reltuples;
+ 
+ 	return result;
+ }
+ 
+ /*
+  * Build an empty bloom index in the initialization fork.
+  */
+ void
+ blbuildempty(Relation index)
+ {
+ 	if (RelationGetNumberOfBlocks(index) != 0)
+ 		elog(ERROR, "index \"%s\" already contains data",
+ 			 RelationGetRelationName(index));
+ 
+ 	/* Initialize the meta page */
+ 	BloomInitMetapage(index);
+ }
+ 
+ /*
+  * Insert new tuple to the bloom index.
+  */
+ bool
+ blinsert(Relation index, Datum *values, bool *isnull,
+ 		 ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique)
+ {
+ 	BloomState	blstate;
+ 	BloomTuple *itup;
+ 	MemoryContext oldCtx;
+ 	MemoryContext insertCtx;
+ 	BloomMetaPageData *metaData;
+ 	Buffer		buffer,
+ 				metaBuffer;
+ 	Page		page,
+ 				metaPage;
+ 	BlockNumber blkno = InvalidBlockNumber;
+ 	OffsetNumber nStart;
+ 	GenericXLogState *state;
+ 
+ 	insertCtx = AllocSetContextCreate(CurrentMemoryContext,
+ 									  "Bloom insert temporary context",
+ 									  ALLOCSET_DEFAULT_MINSIZE,
+ 									  ALLOCSET_DEFAULT_INITSIZE,
+ 									  ALLOCSET_DEFAULT_MAXSIZE);
+ 
+ 	oldCtx = MemoryContextSwitchTo(insertCtx);
+ 
+ 	initBloomState(&blstate, index);
+ 	itup = BloomFormTuple(&blstate, ht_ctid, values, isnull);
+ 
+ 	/*
+ 	 * At first, try to insert new tuple to the first page in notFullPage
+ 	 * array.  If success we don't need to modify the meta page.
+ 	 */
+ 	metaBuffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 	LockBuffer(metaBuffer, BUFFER_LOCK_SHARE);
+ 	metaData = BloomPageGetMeta(BufferGetPage(metaBuffer));
+ 
+ 	if (metaData->nEnd > metaData->nStart)
+ 	{
+ 		Page		page;
+ 
+ 		blkno = metaData->notFullPage[metaData->nStart];
+ 
+ 		Assert(blkno != InvalidBlockNumber);
+ 		LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		state = GenericXLogStart(index);
+ 		page = GenericXLogRegister(state, buffer, false);
+ 
+ 		if (BloomPageAddItem(&blstate, page, itup))
+ 		{
+ 			GenericXLogFinish(state);
+ 			UnlockReleaseBuffer(buffer);
+ 			ReleaseBuffer(metaBuffer);
+ 			MemoryContextSwitchTo(oldCtx);
+ 			MemoryContextDelete(insertCtx);
+ 			return false;
+ 		}
+ 		else
+ 		{
+ 			GenericXLogAbort(state);
+ 			UnlockReleaseBuffer(buffer);
+ 		}
+ 	}
+ 	else
+ 	{
+ 		/* First page in notFullPage isn't suitable */
+ 		LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
+ 	}
+ 
+ 	/*
+ 	 * Try other pages in notFullPage array.  We will have to change nStart in
+ 	 * metapage.  Thus, grab exclusive lock on metapage.
+ 	 */
+ 	LockBuffer(metaBuffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 	state = GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(state, metaBuffer, false);
+ 	metaData = BloomPageGetMeta(metaPage);
+ 
+ 	/*
+ 	 * Iterate over notFullPage array.  Skip page we already tried first.
+ 	 */
+ 	nStart = metaData->nStart;
+ 	if (metaData->nEnd > nStart &&
+ 		blkno == metaData->notFullPage[nStart])
+ 		nStart++;
+ 
+ 	while (metaData->nEnd > nStart)
+ 	{
+ 		blkno = metaData->notFullPage[nStart];
+ 		Assert(blkno != InvalidBlockNumber);
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		page = GenericXLogRegister(state, buffer, false);
+ 
+ 		if (BloomPageAddItem(&blstate, page, itup))
+ 		{
+ 			metaData->nStart = nStart;
+ 			GenericXLogFinish(state);
+ 			UnlockReleaseBuffer(buffer);
+ 			UnlockReleaseBuffer(metaBuffer);
+ 			MemoryContextSwitchTo(oldCtx);
+ 			MemoryContextDelete(insertCtx);
+ 			return false;
+ 		}
+ 		else
+ 		{
+ 			GenericXLogUnregister(state, buffer);
+ 			UnlockReleaseBuffer(buffer);
+ 		}
+ 		nStart++;
+ 	}
+ 
+ 	GenericXLogAbort(state);
+ 
+ 	/*
+ 	 * Didn't find place to insert in notFullPage array.  Allocate new page.
+ 	 */
+ 	buffer = BloomNewBuffer(index);
+ 
+ 	state = GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(state, metaBuffer, false);
+ 	metaData = BloomPageGetMeta(metaPage);
+ 	page = GenericXLogRegister(state, buffer, true);
+ 	BloomInitPage(page, 0);
+ 	BloomPageAddItem(&blstate, page, itup);
+ 
+ 	metaData->nStart = 0;
+ 	metaData->nEnd = 1;
+ 	metaData->notFullPage[0] = BufferGetBlockNumber(buffer);
+ 
+ 	GenericXLogFinish(state);
+ 
+ 	UnlockReleaseBuffer(buffer);
+ 	UnlockReleaseBuffer(metaBuffer);
+ 
+ 	return false;
+ }
diff --git a/contrib/bloom/bloom--1.0.sql b/contrib/bloom/bloom--1.0.sql
new file mode 100644
index ...7fa7513
*** a/contrib/bloom/bloom--1.0.sql
--- b/contrib/bloom/bloom--1.0.sql
***************
*** 0 ****
--- 1,19 ----
+ CREATE OR REPLACE FUNCTION blhandler(internal)
+ RETURNS index_am_handler
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C;
+ 
+ -- Access method
+ CREATE ACCESS METHOD bloom TYPE INDEX HANDLER blhandler;
+ 
+ -- Opclasses
+ 
+ CREATE OPERATOR CLASS int4_ops
+ DEFAULT FOR TYPE int4 USING bloom AS
+ 	OPERATOR	1	=(int4, int4),
+ 	FUNCTION	1	hashint4(int4);
+ 
+ CREATE OPERATOR CLASS text_ops
+ DEFAULT FOR TYPE text USING bloom AS
+ 	OPERATOR	1	=(text, text),
+ 	FUNCTION	1	hashtext(text);
diff --git a/contrib/bloom/bloom.control b/contrib/bloom/bloom.control
new file mode 100644
index ...4d4124b
*** a/contrib/bloom/bloom.control
--- b/contrib/bloom/bloom.control
***************
*** 0 ****
--- 1,5 ----
+ # bloom extension
+ comment = 'bloom access method - signature file based index'
+ default_version = '1.0'
+ module_pathname = '$libdir/bloom'
+ relocatable = true
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
new file mode 100644
index ...50bf99b
*** a/contrib/bloom/bloom.h
--- b/contrib/bloom/bloom.h
***************
*** 0 ****
--- 1,178 ----
+ /*-------------------------------------------------------------------------
+  *
+  * bloom.h
+  *	  Header for bloom index.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/bloom.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef _BLOOM_H_
+ #define _BLOOM_H_
+ 
+ #include "access/amapi.h"
+ #include "access/generic_xlog.h"
+ #include "access/itup.h"
+ #include "access/xlog.h"
+ #include "nodes/relation.h"
+ #include "fmgr.h"
+ 
+ /* Support procedures numbers */
+ #define BLOOM_HASH_PROC			1
+ #define BLOOM_NPROC				1
+ 
+ /* Scan strategies */
+ #define BLOOM_EQUAL_STRATEGY	1
+ #define BLOOM_NSTRATEGIES		1
+ 
+ /* Opaque for bloom pages */
+ typedef struct BloomPageOpaqueData
+ {
+ 	OffsetNumber maxoff;
+ 	uint16		flags;
+ }	BloomPageOpaqueData;
+ 
+ typedef BloomPageOpaqueData *BloomPageOpaque;
+ 
+ /* Bloom page flags */
+ #define BLOOM_META		(1<<0)
+ #define BLOOM_DELETED	(2<<0)
+ 
+ /* Macros for accessing bloom page structures */
+ #define BloomPageGetOpaque(page) ((BloomPageOpaque) PageGetSpecialPointer(page))
+ #define BloomPageGetMaxOffset(page) (BloomPageGetOpaque(page)->maxoff)
+ #define BloomPageIsMeta(page) (BloomPageGetOpaque(page)->flags & BLOOM_META)
+ #define BloomPageIsDeleted(page) (BloomPageGetOpaque(page)->flags & BLOOM_DELETED)
+ #define BloomPageSetDeleted(page) (BloomPageGetOpaque(page)->flags |= BLOOM_DELETED)
+ #define BloomPageSetNonDeleted(page) (BloomPageGetOpaque(page)->flags &= ~BLOOM_DELETED)
+ #define BloomPageGetData(page)		((BloomTuple *)PageGetContents(page))
+ #define BloomPageGetTuple(state, page, offset) \
+ 	((BloomTuple *)(PageGetContents(page) \
+ 		+ (state)->sizeOfBloomTuple * ((offset) - 1)))
+ #define BloomPageGetNextTuple(state, tuple) \
+ 	((BloomTuple *)((Pointer)(tuple) + (state)->sizeOfBloomTuple))
+ 
+ /* Preserved page numbers */
+ #define BLOOM_METAPAGE_BLKNO	(0)
+ #define BLOOM_HEAD_BLKNO		(1)		/* first data page */
+ 
+ /* Bloom index options */
+ typedef struct BloomOptions
+ {
+ 	int32		vl_len_;		/* varlena header (do not touch directly!) */
+ 	int			bloomLength;	/* length of signature in uint16 */
+ 	int			bitSize[INDEX_MAX_KEYS];		/* signature bits per index
+ 												 * key */
+ }	BloomOptions;
+ 
+ /*
+  * FreeBlockNumberArray - array of block numbers sized so that metadata fill
+  * all space in metapage.
+  */
+ typedef BlockNumber FreeBlockNumberArray[
+ 										 MAXALIGN_DOWN(
+ 		BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(BloomPageOpaqueData))
+ 	   - MAXALIGN(sizeof(uint16) * 2 + sizeof(uint32) + sizeof(BloomOptions))
+ 													   ) / sizeof(BlockNumber)
+ ];
+ 
+ /* Metadata of bloom index */
+ typedef struct BloomMetaPageData
+ {
+ 	uint32		magickNumber;
+ 	uint16		nStart;
+ 	uint16		nEnd;
+ 	BloomOptions opts;
+ 	FreeBlockNumberArray notFullPage;
+ }	BloomMetaPageData;
+ 
+ /* Magic number to distinguish bloom pages among anothers */
+ #define BLOOM_MAGICK_NUMBER (0xDBAC0DED)
+ 
+ /* Number of blocks numbers fit in BloomMetaPageData */
+ #define BloomMetaBlockN		(sizeof(FreeBlockNumberArray) / sizeof(BlockNumber))
+ 
+ #define BloomPageGetMeta(page)	((BloomMetaPageData *) PageGetContents(page))
+ 
+ typedef struct BloomState
+ {
+ 	FmgrInfo	hashFn[INDEX_MAX_KEYS];
+ 	BloomOptions *opts;			/* stored in rd_amcache and defined at
+ 								 * creation time */
+ 	int32		nColumns;
+ 
+ 	/*
+ 	 * sizeOfBloomTuple is index's specific, and it depends on reloptions, so
+ 	 * precompute it
+ 	 */
+ 	int32		sizeOfBloomTuple;
+ }	BloomState;
+ 
+ #define BloomPageGetFreeSpace(state, page) \
+ 	(BLCKSZ - MAXALIGN(SizeOfPageHeaderData) \
+ 		- BloomPageGetMaxOffset(page) * (state)->sizeOfBloomTuple \
+ 		- MAXALIGN(sizeof(BloomPageOpaqueData)))
+ 
+ /*
+  * Tuples are very different from all other relations
+  */
+ typedef uint16 SignType;
+ 
+ typedef struct BloomTuple
+ {
+ 	ItemPointerData heapPtr;
+ 	SignType	sign[1];
+ }	BloomTuple;
+ 
+ #define BLOOMTUPLEHDRSZ offsetof(BloomTuple, sign)
+ 
+ /* Opaque data structure for bloom index scan */
+ typedef struct BloomScanOpaqueData
+ {
+ 	SignType   *sign;			/* Scan signature */
+ 	BloomState	state;
+ }	BloomScanOpaqueData;
+ 
+ typedef BloomScanOpaqueData *BloomScanOpaque;
+ 
+ /* blutils.c */
+ extern void _PG_init(void);
+ extern Datum blhandler(PG_FUNCTION_ARGS);
+ extern void initBloomState(BloomState * state, Relation index);
+ extern void BloomInitMetapage(Relation index);
+ extern void BloomInitPage(Page page, uint16 flags);
+ extern Buffer BloomNewBuffer(Relation index);
+ extern void signValue(BloomState * state, SignType * sign, Datum value, int attno);
+ extern BloomTuple *BloomFormTuple(BloomState * state, ItemPointer iptr, Datum *values, bool *isnull);
+ extern bool BloomPageAddItem(BloomState * state, Page page, BloomTuple * tuple);
+ 
+ /* blvalidate.c */
+ extern bool blvalidate(Oid opclassoid);
+ 
+ /* index access method interface functions */
+ extern bool blinsert(Relation index, Datum *values, bool *isnull,
+ 		 ItemPointer ht_ctid, Relation heapRel,
+ 		 IndexUniqueCheck checkUnique);
+ extern IndexScanDesc blbeginscan(Relation r, int nkeys, int norderbys);
+ extern int64 blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
+ extern void blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ 		 ScanKey orderbys, int norderbys);
+ extern void blendscan(IndexScanDesc scan);
+ extern IndexBuildResult *blbuild(Relation heap, Relation index,
+ 		struct IndexInfo *indexInfo);
+ extern void blbuildempty(Relation index);
+ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
+ 			 IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback,
+ 			 void *callback_state);
+ extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
+ 				IndexBulkDeleteResult *stats);
+ extern bytea *bloptions(Datum reloptions, bool validate);
+ extern void blcostestimate(PlannerInfo *root, IndexPath *path,
+ 			   double loop_count, Cost *indexStartupCost,
+ 			   Cost *indexTotalCost, Selectivity *indexSelectivity,
+ 			   double *indexCorrelation);
+ 
+ #endif
diff --git a/contrib/bloom/blscan.c b/contrib/bloom/blscan.c
new file mode 100644
index ...d156e88
*** a/contrib/bloom/blscan.c
--- b/contrib/bloom/blscan.c
***************
*** 0 ****
--- 1,175 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blscan.c
+  *		Bloom index scan functions.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blscan.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/relscan.h"
+ #include "pgstat.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/lmgr.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ 
+ #include "bloom.h"
+ 
+ /*
+  * Begin scan of bloom index.
+  */
+ IndexScanDesc
+ blbeginscan(Relation r, int nkeys, int norderbys)
+ {
+ 	IndexScanDesc scan;
+ 
+ 	scan = RelationGetIndexScan(r, nkeys, norderbys);
+ 
+ 	return scan;
+ }
+ 
+ /*
+  * Rescan a bloom index.
+  */
+ void
+ blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ 		 ScanKey orderbys, int norderbys)
+ {
+ 	BloomScanOpaque so;
+ 
+ 	so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so == NULL)
+ 	{
+ 		/* if called from blbeginscan */
+ 		so = (BloomScanOpaque) palloc(sizeof(BloomScanOpaqueData));
+ 		initBloomState(&so->state, scan->indexRelation);
+ 		scan->opaque = so;
+ 
+ 	}
+ 	else
+ 	{
+ 		if (so->sign)
+ 			pfree(so->sign);
+ 	}
+ 	so->sign = NULL;
+ 
+ 	if (scankey && scan->numberOfKeys > 0)
+ 	{
+ 		memmove(scan->keyData, scankey,
+ 				scan->numberOfKeys * sizeof(ScanKeyData));
+ 	}
+ }
+ 
+ /*
+  * End scan of bloom index.
+  */
+ void
+ blendscan(IndexScanDesc scan)
+ {
+ 	BloomScanOpaque so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so->sign)
+ 		pfree(so->sign);
+ 	so->sign = NULL;
+ }
+ 
+ /*
+  * Insert all matching tuples into to a bitmap.
+  */
+ int64
+ blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
+ {
+ 	int64		ntids = 0;
+ 	BlockNumber blkno = BLOOM_HEAD_BLKNO,
+ 				npages;
+ 	int			i;
+ 	BufferAccessStrategy bas;
+ 	BloomScanOpaque so = (BloomScanOpaque) scan->opaque;
+ 
+ 	if (so->sign == NULL && scan->numberOfKeys > 0)
+ 	{
+ 		/* New search: have to calculate search signature */
+ 		ScanKey		skey = scan->keyData;
+ 
+ 		so->sign = palloc0(sizeof(SignType) * so->state.opts->bloomLength);
+ 
+ 		for (i = 0; i < scan->numberOfKeys; i++)
+ 		{
+ 			/*
+ 			 * Assume bloom-indexable operators to be strict, so nothing could
+ 			 * be found for NULL key.
+ 			 */
+ 			if (skey->sk_flags & SK_ISNULL)
+ 			{
+ 				pfree(so->sign);
+ 				so->sign = NULL;
+ 				return 0;
+ 			}
+ 
+ 			/* Add next value to the signature */
+ 			signValue(&so->state, so->sign, skey->sk_argument,
+ 					  skey->sk_attno - 1);
+ 
+ 			skey++;
+ 		}
+ 	}
+ 
+ 	/*
+ 	 * We're going to read the whole index. This is why we use appropriate
+ 	 * buffer access strategy.
+ 	 */
+ 	bas = GetAccessStrategy(BAS_BULKREAD);
+ 	npages = RelationGetNumberOfBlocks(scan->indexRelation);
+ 
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		Buffer		buffer;
+ 		Page		page;
+ 
+ 		buffer = ReadBufferExtended(scan->indexRelation, MAIN_FORKNUM,
+ 									blkno, RBM_NORMAL, bas);
+ 
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 		page = BufferGetPage(buffer);
+ 
+ 		if (!BloomPageIsDeleted(page))
+ 		{
+ 			OffsetNumber offset,
+ 						maxOffset = BloomPageGetMaxOffset(page);
+ 
+ 			for (offset = 1; offset <= maxOffset; offset++)
+ 			{
+ 				BloomTuple *itup = BloomPageGetTuple(&so->state, page, offset);
+ 				bool		res = true;
+ 
+ 				/* Check index signature with scan signature */
+ 				for (i = 0; res && i < so->state.opts->bloomLength; i++)
+ 				{
+ 					if ((itup->sign[i] & so->sign[i]) != so->sign[i])
+ 						res = false;
+ 				}
+ 
+ 				/* Add matching tuples to bitmap */
+ 				if (res)
+ 				{
+ 					tbm_add_tuples(tbm, &itup->heapPtr, 1, true);
+ 					ntids++;
+ 				}
+ 			}
+ 		}
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 		CHECK_FOR_INTERRUPTS();
+ 	}
+ 	FreeAccessStrategy(bas);
+ 
+ 	return ntids;
+ }
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
new file mode 100644
index ...b86f51f
*** a/contrib/bloom/blutils.c
--- b/contrib/bloom/blutils.c
***************
*** 0 ****
--- 1,463 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blutils.c
+  *		Bloom index utilities.
+  *
+  * Portions Copyright (c) 2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1990-1993, Regents of the University of California
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blutils.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/amapi.h"
+ #include "access/generic_xlog.h"
+ #include "catalog/index.h"
+ #include "storage/lmgr.h"
+ #include "miscadmin.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "utils/memutils.h"
+ #include "access/reloptions.h"
+ #include "storage/freespace.h"
+ #include "storage/indexfsm.h"
+ 
+ #include "bloom.h"
+ 
+ /* Signature dealing macros */
+ #define BITSIGNTYPE (BITS_PER_BYTE * sizeof(SignType))
+ #define GETWORD(x,i) ( *( (SignType*)(x) + (int)( (i) / BITSIGNTYPE ) ) )
+ #define CLRBIT(x,i)   GETWORD(x,i) &= ~( 0x01 << ( (i) % BITSIGNTYPE ) )
+ #define SETBIT(x,i)   GETWORD(x,i) |=  ( 0x01 << ( (i) % BITSIGNTYPE ) )
+ #define GETBIT(x,i) ( (GETWORD(x,i) >> ( (i) % BITSIGNTYPE )) & 0x01 )
+ 
+ PG_FUNCTION_INFO_V1(blhandler);
+ 
+ /* Kind of relation optioms for bloom index */
+ static relopt_kind bl_relopt_kind;
+ 
+ static int32 myRand();
+ static void mySrand(uint32 seed);
+ 
+ /*
+  * Module initialize function: initilized relation options.
+  */
+ void
+ _PG_init(void)
+ {
+ 	int			i;
+ 	char		buf[16];
+ 
+ 	bl_relopt_kind = add_reloption_kind();
+ 
+ 	add_int_reloption(bl_relopt_kind, "length",
+ 					  "Length of signature in uint16 type", 5, 1, 256);
+ 
+ 	for (i = 0; i < INDEX_MAX_KEYS; i++)
+ 	{
+ 		snprintf(buf, 16, "col%d", i + 1);
+ 		add_int_reloption(bl_relopt_kind, buf,
+ 					  "Number of bits for corresponding column", 2, 1, 2048);
+ 	}
+ }
+ 
+ /*
+  * Bloom handler function: return IndexAmRoutine with access method parameters
+  * and callbacks.
+  */
+ Datum
+ blhandler(PG_FUNCTION_ARGS)
+ {
+ 	IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
+ 
+ 	amroutine->amstrategies = 1;
+ 	amroutine->amsupport = 1;
+ 	amroutine->amcanorder = false;
+ 	amroutine->amcanorderbyop = false;
+ 	amroutine->amcanbackward = false;
+ 	amroutine->amcanunique = false;
+ 	amroutine->amcanmulticol = true;
+ 	amroutine->amoptionalkey = true;
+ 	amroutine->amsearcharray = false;
+ 	amroutine->amsearchnulls = false;
+ 	amroutine->amstorage = false;
+ 	amroutine->amclusterable = false;
+ 	amroutine->ampredlocks = false;
+ 	amroutine->amkeytype = 0;
+ 
+ 	amroutine->aminsert = blinsert;
+ 	amroutine->ambeginscan = blbeginscan;
+ 	amroutine->amgettuple = NULL;
+ 	amroutine->amgetbitmap = blgetbitmap;
+ 	amroutine->amrescan = blrescan;
+ 	amroutine->amendscan = blendscan;
+ 	amroutine->ammarkpos = NULL;
+ 	amroutine->amrestrpos = NULL;
+ 	amroutine->ambuild = blbuild;
+ 	amroutine->ambuildempty = blbuildempty;
+ 	amroutine->ambulkdelete = blbulkdelete;
+ 	amroutine->amvacuumcleanup = blvacuumcleanup;
+ 	amroutine->amcanreturn = NULL;
+ 	amroutine->amcostestimate = blcostestimate;
+ 	amroutine->amoptions = bloptions;
+ 	amroutine->amvalidate = blvalidate;
+ 
+ 	PG_RETURN_POINTER(amroutine);
+ }
+ 
+ /*
+  * Fill BloomState structure for particular index.
+  */
+ void
+ initBloomState(BloomState *state, Relation index)
+ {
+ 	int			i;
+ 
+ 	state->nColumns = index->rd_att->natts;
+ 
+ 	/* Initialize hash function for each attribute */
+ 	for (i = 0; i < index->rd_att->natts; i++)
+ 	{
+ 		fmgr_info_copy(&(state->hashFn[i]),
+ 					   index_getprocinfo(index, i + 1, BLOOM_HASH_PROC),
+ 					   CurrentMemoryContext);
+ 	}
+ 
+ 	/* Initialize amcache if needed with options from metapage */
+ 	if (!index->rd_amcache)
+ 	{
+ 		Buffer		buffer;
+ 		Page		page;
+ 		BloomMetaPageData *meta;
+ 		BloomOptions *opts;
+ 
+ 		opts = MemoryContextAlloc(index->rd_indexcxt, sizeof(BloomOptions));
+ 
+ 		buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 
+ 		page = BufferGetPage(buffer);
+ 
+ 		if (!BloomPageIsMeta(page))
+ 			elog(ERROR, "Relation is not a bloom index");
+ 		meta = BloomPageGetMeta(BufferGetPage(buffer));
+ 
+ 		if (meta->magickNumber != BLOOM_MAGICK_NUMBER)
+ 			elog(ERROR, "Relation is not a bloom index");
+ 
+ 		*opts = meta->opts;
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 
+ 		index->rd_amcache = (void *) opts;
+ 	}
+ 
+ 	state->opts = (BloomOptions *) index->rd_amcache;
+ 	state->sizeOfBloomTuple = BLOOMTUPLEHDRSZ +
+ 		sizeof(SignType) * state->opts->bloomLength;
+ }
+ 
+ /*
+  * Random generator copied from FreeBSD.  Using own random generator here for
+  * two reasons:
+  *
+  * 1) In this case random numbers are used for on-disk storage.  Usage of
+  *	  PostgreSQL number generator would obstruct it from all possible changes.
+  * 2) Changing seed of PostgreSQL random generator would be undesirable side
+  *	  effect.
+  */
+ static int32 next;
+ 
+ static int32
+ myRand()
+ {
+ 	/*
+ 	 * Compute x = (7^5 * x) mod (2^31 - 1)
+ 	 * without overflowing 31 bits:
+ 	 *      (2^31 - 1) = 127773 * (7^5) + 2836
+ 	 * From "Random number generators: good ones are hard to find",
+ 	 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
+ 	 * October 1988, p. 1195.
+ 	 */
+ 	int32 hi, lo, x;
+ 
+ 	/* Must be in [1, 0x7ffffffe] range at this point. */
+ 	hi = next / 127773;
+ 	lo = next % 127773;
+ 	x = 16807 * lo - 2836 * hi;
+ 	if (x < 0)
+ 		x += 0x7fffffff;
+ 	next = x;
+ 	/* Transform to [0, 0x7ffffffd] range. */
+ 	return (x - 1);
+ }
+ 
+ void
+ mySrand(uint32 seed)
+ {
+ 	next = seed;
+ 	/* Transform to [1, 0x7ffffffe] range. */
+ 	next = (next % 0x7ffffffe) + 1;
+ }
+ 
+ /*
+  * Add bits of given value to the signature.
+  */
+ void
+ signValue(BloomState *state, SignType *sign, Datum value, int attno)
+ {
+ 	uint32		hashVal;
+ 	int			nBit,
+ 				j;
+ 
+ 	/*
+ 	 * init generator with "column's" number to get "hashed" seed for new
+ 	 * value. We don't want to map the same numbers from different columns
+ 	 * into the same bits!
+ 	 */
+ 	mySrand(attno);
+ 
+ 	/*
+ 	 * Init hash sequence to map our value into bits. the same values in
+ 	 * different columns will be mapped into different bits because of step
+ 	 * above
+ 	 */
+ 	hashVal = DatumGetInt32(FunctionCall1(&state->hashFn[attno], value));
+ 	mySrand(hashVal ^ myRand());
+ 
+ 	for (j = 0; j < state->opts->bitSize[attno]; j++)
+ 	{
+ 		/* prevent mutiple evaluation */
+ 		nBit = myRand() % (state->opts->bloomLength * BITSIGNTYPE);
+ 		SETBIT(sign, nBit);
+ 	}
+ }
+ 
+ /*
+  * Make bloom tuple from values.
+  */
+ BloomTuple *
+ BloomFormTuple(BloomState *state, ItemPointer iptr, Datum *values, bool *isnull)
+ {
+ 	int			i;
+ 	BloomTuple *res = (BloomTuple *) palloc0(state->sizeOfBloomTuple);
+ 
+ 	res->heapPtr = *iptr;
+ 
+ 	/* Blooming each column */
+ 	for (i = 0; i < state->nColumns; i++)
+ 	{
+ 		/* skip nulls */
+ 		if (isnull[i])
+ 			continue;
+ 
+ 		signValue(state, res->sign, values[i], i);
+ 	}
+ 
+ 	return res;
+ }
+ 
+ /*
+  * Add new bloom tuple to the page.  Returns true if new tuple was successfully
+  * added to the page.  Returns false if it doesn't git the page.
+  */
+ bool
+ BloomPageAddItem(BloomState *state, Page page, BloomTuple *tuple)
+ {
+ 	BloomTuple *itup;
+ 	BloomPageOpaque opaque;
+ 	Pointer		ptr;
+ 
+ 	/* Does new tuple fit the page */
+ 	if (BloomPageGetFreeSpace(state, page) < state->sizeOfBloomTuple)
+ 		return false;
+ 
+ 	/* Copy new tuple to the end of page */
+ 	opaque = BloomPageGetOpaque(page);
+ 	itup = BloomPageGetTuple(state, page, opaque->maxoff + 1);
+ 	memcpy((Pointer) itup, (Pointer) tuple, state->sizeOfBloomTuple);
+ 
+ 	/* Adjust maxoff and pd_lower */
+ 	opaque->maxoff++;
+ 	ptr = (Pointer) BloomPageGetTuple(state, page, opaque->maxoff + 1);
+ 	((PageHeader) page)->pd_lower = ptr - page;
+ 
+ 	return true;
+ }
+ 
+ /*
+  * Allocate a new page (either by recycling, or by extending the index file)
+  * The returned buffer is already pinned and exclusive-locked
+  * Caller is responsible for initializing the page by calling BloomInitBuffer
+  */
+ Buffer
+ BloomNewBuffer(Relation index)
+ {
+ 	Buffer		buffer;
+ 	bool		needLock;
+ 
+ 	/* First, try to get a page from FSM */
+ 	for (;;)
+ 	{
+ 		BlockNumber blkno = GetFreeIndexPage(index);
+ 
+ 		if (blkno == InvalidBlockNumber)
+ 			break;
+ 
+ 		buffer = ReadBuffer(index, blkno);
+ 
+ 		/*
+ 		 * We have to guard against the possibility that someone else already
+ 		 * recycled this page; the buffer may be locked if so.
+ 		 */
+ 		if (ConditionalLockBuffer(buffer))
+ 		{
+ 			Page		page = BufferGetPage(buffer);
+ 
+ 			if (PageIsNew(page))
+ 				return buffer;	/* OK to use, if never initialized */
+ 
+ 			if (BloomPageIsDeleted(page))
+ 				return buffer;	/* OK to use */
+ 
+ 			LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
+ 		}
+ 
+ 		/* Can't use it, so release buffer and try again */
+ 		ReleaseBuffer(buffer);
+ 	}
+ 
+ 	/* Must extend the file */
+ 	needLock = !RELATION_IS_LOCAL(index);
+ 	if (needLock)
+ 		LockRelationForExtension(index, ExclusiveLock);
+ 
+ 	buffer = ReadBuffer(index, P_NEW);
+ 	LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 	if (needLock)
+ 		UnlockRelationForExtension(index, ExclusiveLock);
+ 
+ 	return buffer;
+ }
+ 
+ /*
+  * Initialize bloom page.
+  */
+ void
+ BloomInitPage(Page page, uint16 flags)
+ {
+ 	BloomPageOpaque opaque;
+ 
+ 	PageInit(page, BLCKSZ, sizeof(BloomPageOpaqueData));
+ 
+ 	opaque = BloomPageGetOpaque(page);
+ 	memset(opaque, 0, sizeof(BloomPageOpaqueData));
+ 	opaque->flags = flags;
+ }
+ 
+ /*
+  * Adjust options of bloom index.
+  */
+ static void
+ adjustBloomOptions(BloomOptions *opts)
+ {
+ 	int				i;
+ 
+ 	/* Default length of bloom filter is 5 of 16-bit integers */
+ 	if (opts->bloomLength <= 0)
+ 		opts->bloomLength = 5;
+ 	else
+ 		opts->bloomLength = opts->bloomLength;
+ 
+ 	/* Check singnature length */
+ 	for (i = 0; i < INDEX_MAX_KEYS; i++)
+ 	{
+ 		/*
+ 		 * Zero and negative number of bits is meaningless.  Also setting
+ 		 * more bits than signature have seems useless.  Replace both cases
+ 		 * with 2 bits default.
+ 		 */
+ 		if (opts->bitSize[i] <= 0
+ 			|| opts->bitSize[i] >= opts->bloomLength * sizeof(SignType))
+ 			opts->bitSize[i] = 2;
+ 	}
+ }
+ 
+ /*
+  * Initialize metapage for bloom index.
+  */
+ void
+ BloomInitMetapage(Relation index)
+ {
+ 	Page		metaPage;
+ 	Buffer		metaBuffer;
+ 	BloomMetaPageData *metadata;
+ 	GenericXLogState *state;
+ 
+ 	/*
+ 	 * Make a new buffer, since it first buffer it should be associated with
+ 	 * block number 0 (BLOOM_METAPAGE_BLKNO).
+ 	 */
+ 	metaBuffer = BloomNewBuffer(index);
+ 	Assert(BufferGetBlockNumber(metaBuffer) == BLOOM_METAPAGE_BLKNO);
+ 
+ 	/* Initialize bloom index options */
+ 	if (!index->rd_options)
+ 		index->rd_options = palloc0(sizeof(BloomOptions));
+ 	adjustBloomOptions((BloomOptions *) index->rd_options);
+ 
+ 	/* Initialize contents of meta page */
+ 	state = GenericXLogStart(index);
+ 	metaPage = GenericXLogRegister(state, metaBuffer, true);
+ 
+ 	BloomInitPage(metaPage, BLOOM_META);
+ 	metadata = BloomPageGetMeta(metaPage);
+ 	memset(metadata, 0, sizeof(BloomMetaPageData));
+ 	metadata->magickNumber = BLOOM_MAGICK_NUMBER;
+ 	metadata->opts = *((BloomOptions *) index->rd_options);
+ 	((PageHeader) metaPage)->pd_lower += sizeof(BloomMetaPageData);
+ 
+ 	GenericXLogFinish(state);
+ 	UnlockReleaseBuffer(metaBuffer);
+ }
+ 
+ /*
+  * Initialize options for bloom index.
+  */
+ bytea *
+ bloptions(Datum reloptions, bool validate)
+ {
+ 	relopt_value *options;
+ 	int			numoptions;
+ 	BloomOptions *rdopts;
+ 	relopt_parse_elt tab[INDEX_MAX_KEYS + 1];
+ 	int			i;
+ 	char		buf[16];
+ 
+ 	/* Option for length of signature */
+ 	tab[0].optname = "length";
+ 	tab[0].opttype = RELOPT_TYPE_INT;
+ 	tab[0].offset = offsetof(BloomOptions, bloomLength);
+ 
+ 	/* Number of bits for each of possible columns: col1, col2, ... */
+ 	for (i = 0; i < INDEX_MAX_KEYS; i++)
+ 	{
+ 		snprintf(buf, sizeof(buf), "col%d", i + 1);
+ 		tab[i + 1].optname = pstrdup(buf);
+ 		tab[i + 1].opttype = RELOPT_TYPE_INT;
+ 		tab[i + 1].offset = offsetof(BloomOptions, bitSize[i]);
+ 	}
+ 
+ 	options = parseRelOptions(reloptions, validate, bl_relopt_kind, &numoptions);
+ 	rdopts = allocateReloptStruct(sizeof(BloomOptions), options, numoptions);
+ 	fillRelOptions((void *) rdopts, sizeof(BloomOptions), options, numoptions,
+ 				   validate, tab, INDEX_MAX_KEYS + 1);
+ 
+ 	adjustBloomOptions(rdopts);
+ 
+ 	return (bytea *) rdopts;
+ }
diff --git a/contrib/bloom/blvacuum.c b/contrib/bloom/blvacuum.c
new file mode 100644
index ...fb8d9b8
*** a/contrib/bloom/blvacuum.c
--- b/contrib/bloom/blvacuum.c
***************
*** 0 ****
--- 1,212 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blvacuum.c
+  *		Bloom VACUUM functions.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blvacuum.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "catalog/storage.h"
+ #include "commands/vacuum.h"
+ #include "miscadmin.h"
+ #include "postmaster/autovacuum.h"
+ #include "storage/bufmgr.h"
+ #include "storage/indexfsm.h"
+ #include "storage/lmgr.h"
+ 
+ #include "bloom.h"
+ 
+ /*
+  * Bulk deletion of all index entries pointing to a set of heap tuples.
+  * The set of target tuples is specified via a callback routine that tells
+  * whether any given heap tuple (identified by ItemPointer) is being deleted.
+  *
+  * Result: a palloc'd struct containing statistical info for VACUUM displays.
+  */
+ IndexBulkDeleteResult *
+ blbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
+ 			 IndexBulkDeleteCallback callback, void *callback_state)
+ {
+ 	Relation	index = info->index;
+ 	BlockNumber blkno,
+ 				npages;
+ 	FreeBlockNumberArray notFullPage;
+ 	int			countPage = 0;
+ 	BloomState	state;
+ 	Buffer		buffer;
+ 	Page		page;
+ 	GenericXLogState *gxlogState;
+ 
+ 	if (stats == NULL)
+ 		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+ 
+ 	initBloomState(&state, index);
+ 
+ 	/*
+ 	 * Interate over the pages. We don't care about concurrently added pages,
+ 	 * they can't contain tuples to delete.
+ 	 */
+ 	npages = RelationGetNumberOfBlocks(index);
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		BloomTuple *itup,
+ 				   *itupPtr,
+ 				   *itupEnd;
+ 
+ 		buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
+ 									RBM_NORMAL, info->strategy);
+ 
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 		gxlogState = GenericXLogStart(index);
+ 		page = GenericXLogRegister(gxlogState, buffer, false);
+ 
+ 		if (BloomPageIsDeleted(page))
+ 		{
+ 			UnlockReleaseBuffer(buffer);
+ 			CHECK_FOR_INTERRUPTS();
+ 			continue;
+ 		}
+ 
+ 		/* Iterate over the tuples */
+ 		itup = BloomPageGetTuple(&state, page, 1);
+ 		itupPtr = BloomPageGetTuple(&state, page, 1);
+ 		itupEnd = BloomPageGetTuple(&state, page, BloomPageGetMaxOffset(page) + 1);
+ 		while (itup < itupEnd)
+ 		{
+ 			/* Do we have to delete this tuple? */
+ 			if (callback(&itup->heapPtr, callback_state))
+ 			{
+ 				stats->tuples_removed += 1;
+ 				BloomPageGetOpaque(page)->maxoff--;
+ 			}
+ 			else
+ 			{
+ 				if (itupPtr != itup)
+ 				{
+ 					/*
+ 					 * If we already delete something before, we have to move
+ 					 * this tuple backward.
+ 					 */
+ 					memmove((Pointer) itupPtr, (Pointer) itup,
+ 							state.sizeOfBloomTuple);
+ 				}
+ 				stats->num_index_tuples++;
+ 				itupPtr = BloomPageGetNextTuple(&state, itupPtr);
+ 			}
+ 
+ 			itup = BloomPageGetNextTuple(&state, itup);
+ 		}
+ 
+ 		Assert(itupPtr == BloomPageGetTuple(&state, page, BloomPageGetMaxOffset(page) + 1));
+ 
+ 		if (!BloomPageIsDeleted(page) &&
+ 			BloomPageGetFreeSpace(&state, page) > state.sizeOfBloomTuple &&
+ 			countPage < BloomMetaBlockN)
+ 			notFullPage[countPage++] = blkno;
+ 
+ 		/* Did we delete something? */
+ 		if (itupPtr != itup)
+ 		{
+ 			/* Is it empty page now? */
+ 			if (itupPtr == BloomPageGetData(page))
+ 				BloomPageSetDeleted(page);
+ 			/* Adjust pg_lower */
+ 			((PageHeader) page)->pd_lower = (Pointer) itupPtr - page;
+ 			/* Finish WAL-logging */
+ 			GenericXLogFinish(gxlogState);
+ 		}
+ 		else
+ 		{
+ 			/* Didn't change anything: abort WAL-logging */
+ 			GenericXLogAbort(gxlogState);
+ 		}
+ 		UnlockReleaseBuffer(buffer);
+ 		CHECK_FOR_INTERRUPTS();
+ 	}
+ 
+ 	if (countPage > 0)
+ 	{
+ 		BloomMetaPageData *metaData;
+ 
+ 		buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+ 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 		gxlogState = GenericXLogStart(index);
+ 		page = GenericXLogRegister(gxlogState, buffer, false);
+ 
+ 		metaData = BloomPageGetMeta(page);
+ 		memcpy(metaData->notFullPage, notFullPage, sizeof(FreeBlockNumberArray));
+ 		metaData->nStart = 0;
+ 		metaData->nEnd = countPage;
+ 
+ 		GenericXLogFinish(gxlogState);
+ 		UnlockReleaseBuffer(buffer);
+ 	}
+ 
+ 	return stats;
+ }
+ 
+ /*
+  * Post-VACUUM cleanup.
+  *
+  * Result: a palloc'd struct containing statistical info for VACUUM displays.
+  */
+ IndexBulkDeleteResult *
+ blvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
+ {
+ 	Relation	index = info->index;
+ 	BlockNumber npages,
+ 				blkno;
+ 	BlockNumber totFreePages;
+ 
+ 	if (info->analyze_only)
+ 		return stats;
+ 
+ 	if (stats == NULL)
+ 		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+ 
+ 	/*
+ 	 * Iterate over the pages: insert deleted pages into FSM and collect
+ 	 * statistics.
+ 	 */
+ 	npages = RelationGetNumberOfBlocks(index);
+ 	totFreePages = 0;
+ 	for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
+ 	{
+ 		Buffer		buffer;
+ 		Page		page;
+ 
+ 		vacuum_delay_point();
+ 
+ 		buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
+ 									RBM_NORMAL, info->strategy);
+ 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+ 		page = (Page) BufferGetPage(buffer);
+ 
+ 		if (BloomPageIsDeleted(page))
+ 		{
+ 			RecordFreeIndexPage(index, blkno);
+ 			totFreePages++;
+ 		}
+ 		else
+ 		{
+ 			stats->num_index_tuples += BloomPageGetMaxOffset(page);
+ 			stats->estimated_count += BloomPageGetMaxOffset(page);
+ 		}
+ 
+ 		UnlockReleaseBuffer(buffer);
+ 	}
+ 
+ 	IndexFreeSpaceMapVacuum(info->index);
+ 	stats->pages_free = totFreePages;
+ 	stats->num_pages = RelationGetNumberOfBlocks(index);
+ 
+ 	return stats;
+ }
diff --git a/contrib/bloom/blvalidate.c b/contrib/bloom/blvalidate.c
new file mode 100644
index ...12e7c7d
*** a/contrib/bloom/blvalidate.c
--- b/contrib/bloom/blvalidate.c
***************
*** 0 ****
--- 1,220 ----
+ /*-------------------------------------------------------------------------
+  *
+  * blvalidate.c
+  *	  Opclass validator for bloom.
+  *
+  * Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *	  contrib/bloom/blvalidate.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/amvalidate.h"
+ #include "access/htup_details.h"
+ #include "catalog/pg_amop.h"
+ #include "catalog/pg_amproc.h"
+ #include "catalog/pg_opclass.h"
+ #include "catalog/pg_opfamily.h"
+ #include "catalog/pg_type.h"
+ #include "utils/builtins.h"
+ #include "utils/lsyscache.h"
+ #include "utils/syscache.h"
+ 
+ #include "bloom.h"
+ 
+ /*
+  * Validator for a bloom opclass.
+  */
+ bool
+ blvalidate(Oid opclassoid)
+ {
+ 	bool		result = true;
+ 	HeapTuple	classtup;
+ 	Form_pg_opclass classform;
+ 	Oid			opfamilyoid;
+ 	Oid			opcintype;
+ 	Oid			opckeytype;
+ 	char	   *opclassname;
+ 	HeapTuple	familytup;
+ 	Form_pg_opfamily familyform;
+ 	char	   *opfamilyname;
+ 	CatCList   *proclist,
+ 			   *oprlist;
+ 	List	   *grouplist;
+ 	OpFamilyOpFuncGroup *opclassgroup;
+ 	int			i;
+ 	ListCell   *lc;
+ 
+ 	/* Fetch opclass information */
+ 	classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
+ 	if (!HeapTupleIsValid(classtup))
+ 		elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
+ 	classform = (Form_pg_opclass) GETSTRUCT(classtup);
+ 
+ 	opfamilyoid = classform->opcfamily;
+ 	opcintype = classform->opcintype;
+ 	opckeytype = classform->opckeytype;
+ 	if (!OidIsValid(opckeytype))
+ 		opckeytype = opcintype;
+ 	opclassname = NameStr(classform->opcname);
+ 
+ 	/* Fetch opfamily information */
+ 	familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
+ 	if (!HeapTupleIsValid(familytup))
+ 		elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
+ 	familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
+ 
+ 	opfamilyname = NameStr(familyform->opfname);
+ 
+ 	/* Fetch all operators and support functions of the opfamily */
+ 	oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
+ 	proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
+ 
+ 	/* Check individual support functions */
+ 	for (i = 0; i < proclist->n_members; i++)
+ 	{
+ 		HeapTuple	proctup = &proclist->members[i]->tuple;
+ 		Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
+ 		bool		ok;
+ 
+ 		/*
+ 		 * All bloom support functions should be registered with matching
+ 		 * left/right types
+ 		 */
+ 		if (procform->amproclefttype != procform->amprocrighttype)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains support procedure %s with cross-type registration",
+ 							opfamilyname,
+ 							format_procedure(procform->amproc))));
+ 			result = false;
+ 		}
+ 
+ 		/*
+ 		 * We can't check signatures except within the specific opclass, since
+ 		 * we need to know the associated opckeytype in many cases.
+ 		 */
+ 		if (procform->amproclefttype != opcintype)
+ 			continue;
+ 
+ 		/* Check procedure numbers and function signatures */
+ 		switch (procform->amprocnum)
+ 		{
+ 			case BLOOM_HASH_PROC:
+ 				ok = check_amproc_signature(procform->amproc, INT4OID, false,
+ 											1, 1, opckeytype);
+ 				break;
+ 			default:
+ 				ereport(INFO,
+ 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 						 errmsg("bloom opfamily %s contains function %s with invalid support number %d",
+ 								opfamilyname,
+ 								format_procedure(procform->amproc),
+ 								procform->amprocnum)));
+ 				result = false;
+ 				continue;		/* don't want additional message */
+ 		}
+ 
+ 		if (!ok)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("gist opfamily %s contains function %s with wrong signature for support number %d",
+ 							opfamilyname,
+ 							format_procedure(procform->amproc),
+ 							procform->amprocnum)));
+ 			result = false;
+ 		}
+ 	}
+ 
+ 	/* Check individual operators */
+ 	for (i = 0; i < oprlist->n_members; i++)
+ 	{
+ 		HeapTuple	oprtup = &oprlist->members[i]->tuple;
+ 		Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
+ 
+ 		/* Check it's allowed strategy for bloom */
+ 		if (oprform->amopstrategy < 1 ||
+ 			oprform->amopstrategy > BLOOM_NSTRATEGIES)
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains operator %s with invalid strategy number %d",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr),
+ 							oprform->amopstrategy)));
+ 			result = false;
+ 		}
+ 
+ 		/* bloom doesn't support ORDER BY operators */
+ 		if (oprform->amoppurpose != AMOP_SEARCH ||
+ 			OidIsValid(oprform->amopsortfamily))
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains invalid ORDER BY specification for operator %s",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr))));
+ 			result = false;
+ 		}
+ 
+ 		/* Check operator signature --- same for all bloom strategies */
+ 		if (!check_amop_signature(oprform->amopopr, BOOLOID,
+ 								  oprform->amoplefttype,
+ 								  oprform->amoprighttype))
+ 		{
+ 			ereport(INFO,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("bloom opfamily %s contains operator %s with wrong signature",
+ 							opfamilyname,
+ 							format_operator(oprform->amopopr))));
+ 			result = false;
+ 		}
+ 	}
+ 
+ 	/* Now check for inconsistent groups of operators/functions */
+ 	grouplist = identify_opfamily_groups(oprlist, proclist);
+ 	opclassgroup = NULL;
+ 	foreach(lc, grouplist)
+ 	{
+ 		OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
+ 
+ 		/* Remember the group exactly matching the test opclass */
+ 		if (thisgroup->lefttype == opcintype &&
+ 			thisgroup->righttype == opcintype)
+ 			opclassgroup = thisgroup;
+ 
+ 		/*
+ 		 * There is not a lot we can do to check the operator sets, since each
+ 		 * bloom opclass is more or less a law unto itself, and some contain
+ 		 * only operators that are binary-compatible with the opclass datatype
+ 		 * (meaning that empty operator sets can be OK).  That case also means
+ 		 * that we shouldn't insist on nonempty function sets except for the
+ 		 * opclass's own group.
+ 		 */
+ 	}
+ 
+ 	/* Check that the originally-named opclass is complete */
+ 	for (i = 1; i <= BLOOM_NPROC; i++)
+ 	{
+ 		if (opclassgroup &&
+ 			(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
+ 			continue;			/* got it */
+ 		ereport(INFO,
+ 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 				 errmsg("bloom opclass %s is missing support function %d",
+ 						opclassname, i)));
+ 		result = false;
+ 	}
+ 
+ 	ReleaseCatCacheList(proclist);
+ 	ReleaseCatCacheList(oprlist);
+ 	ReleaseSysCache(familytup);
+ 	ReleaseSysCache(classtup);
+ 
+ 	return result;
+ }
diff --git a/contrib/bloom/expected/bloom.out b/contrib/bloom/expected/bloom.out
new file mode 100644
index ...5e8269f
*** a/contrib/bloom/expected/bloom.out
--- b/contrib/bloom/expected/bloom.out
***************
*** 0 ****
--- 1,122 ----
+ CREATE EXTENSION bloom;
+ CREATE TABLE tst (
+ 	i	int4,
+ 	t	text
+ );
+ INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,100000) i;
+ CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+ SET enable_seqscan=on;
+ SET enable_bitmapscan=off;
+ SET enable_indexscan=off;
+ SELECT count(*) FROM tst WHERE i = 7;
+  count 
+ -------
+  10000
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   6264
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
+  count 
+ -------
+    588
+ (1 row)
+ 
+ SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 7;
+                 QUERY PLAN                 
+ -------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: (i = 7)
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: (i = 7)
+ (5 rows)
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE t = '5';
+                 QUERY PLAN                 
+ -------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: (t = '5'::text)
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: (t = '5'::text)
+ (5 rows)
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
+                        QUERY PLAN                        
+ ---------------------------------------------------------
+  Aggregate
+    ->  Bitmap Heap Scan on tst
+          Recheck Cond: ((i = 7) AND (t = '5'::text))
+          ->  Bitmap Index Scan on bloomidx
+                Index Cond: ((i = 7) AND (t = '5'::text))
+ (5 rows)
+ 
+ SELECT count(*) FROM tst WHERE i = 7;
+  count 
+ -------
+  10000
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   6264
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
+  count 
+ -------
+    588
+ (1 row)
+ 
+ DELETE FROM tst;
+ INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,100000) i;
+ VACUUM ANALYZE tst;
+ SELECT count(*) FROM tst WHERE i = 7;
+  count 
+ -------
+  10000
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   6264
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
+  count 
+ -------
+    588
+ (1 row)
+ 
+ VACUUM FULL tst;
+ SELECT count(*) FROM tst WHERE i = 7;
+  count 
+ -------
+  10000
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE t = '5';
+  count 
+ -------
+   6264
+ (1 row)
+ 
+ SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
+  count 
+ -------
+    588
+ (1 row)
+ 
+ RESET enable_seqscan;
+ RESET enable_bitmapscan;
+ RESET enable_indexscan;
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
new file mode 100644
index ...f9d0ad4
*** a/contrib/bloom/sql/bloom.sql
--- b/contrib/bloom/sql/bloom.sql
***************
*** 0 ****
--- 1,47 ----
+ CREATE EXTENSION bloom;
+ 
+ CREATE TABLE tst (
+ 	i	int4,
+ 	t	text
+ );
+ 
+ INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,100000) i;
+ CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+ 
+ SET enable_seqscan=on;
+ SET enable_bitmapscan=off;
+ SET enable_indexscan=off;
+ 
+ SELECT count(*) FROM tst WHERE i = 7;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
+ 
+ SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ 
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 7;
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE t = '5';
+ EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
+ 
+ SELECT count(*) FROM tst WHERE i = 7;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
+ 
+ DELETE FROM tst;
+ INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,100000) i;
+ VACUUM ANALYZE tst;
+ 
+ SELECT count(*) FROM tst WHERE i = 7;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
+ 
+ VACUUM FULL tst;
+ 
+ SELECT count(*) FROM tst WHERE i = 7;
+ SELECT count(*) FROM tst WHERE t = '5';
+ SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
+ 
+ RESET enable_seqscan;
+ RESET enable_bitmapscan;
+ RESET enable_indexscan;
diff --git a/contrib/bloom/t/001_wal.pl b/contrib/bloom/t/001_wal.pl
new file mode 100644
index ...dbb6a90
*** a/contrib/bloom/t/001_wal.pl
--- b/contrib/bloom/t/001_wal.pl
***************
*** 0 ****
--- 1,75 ----
+ # Test generic xlog record work for bloom index replication.
+ use strict;
+ use warnings;
+ use PostgresNode;
+ use TestLib;
+ use Test::More tests => 31;
+ 
+ my $node_master;
+ my $node_standby;
+ 
+ # Run few queries on both master and standby and check their results match.
+ sub test_index_replay
+ {
+ 	my ($test_name) = @_;
+ 
+ 	# Wait for standby to catch up
+ 	my $applname = $node_standby->name;
+ 	my $caughtup_query =
+ 		"SELECT pg_current_xlog_location() <= write_location FROM pg_stat_replication WHERE application_name = '$applname';";
+ 	$node_master->poll_query_until('postgres', $caughtup_query)
+ 	  or die "Timed out while waiting for standby 1 to catch up";
+ 
+ 	my $queries = qq(SET enable_seqscan=off;
+ SET enable_bitmapscan=on;
+ SET enable_indexscan=on;
+ SELECT * FROM tst WHERE i = 0;
+ SELECT * FROM tst WHERE i = 3;
+ SELECT * FROM tst WHERE t = 'b';
+ SELECT * FROM tst WHERE t = 'f';
+ SELECT * FROM tst WHERE i = 3 AND t = 'c';
+ SELECT * FROM tst WHERE i = 7 AND t = 'e';
+ );
+ 
+ 	# Run test queries and compare their result
+ 	my $master_result = $node_master->psql("postgres", $queries);
+ 	my $standby_result = $node_standby->psql("postgres", $queries);
+ 
+ 	is($master_result, $standby_result, "$test_name: query result matches");
+ }
+ 
+ # Initialize master node
+ $node_master = get_new_node('master');
+ $node_master->init(allows_streaming => 1);
+ $node_master->start;
+ my $backup_name = 'my_backup';
+ 
+ # Take backup
+ $node_master->backup($backup_name);
+ 
+ # Create streaming standby linking to master
+ $node_standby = get_new_node('standby');
+ $node_standby->init_from_backup($node_master, $backup_name,
+ 	has_streaming => 1);
+ $node_standby->start;
+ 
+ # Create some bloom index on master
+ $node_master->psql("postgres", "CREATE EXTENSION bloom;");
+ $node_master->psql("postgres", "CREATE TABLE tst (i int4, t text);");
+ $node_master->psql("postgres", "INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,100000) i;");
+ $node_master->psql("postgres", "CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);");
+ 
+ # Test that queries give same result
+ test_index_replay('initial');
+ 
+ # Run 10 cycles of table modification. Run test queries after each modification.
+ for my $i (1..10)
+ {
+ 	$node_master->psql("postgres", "DELETE FROM tst WHERE i = $i;");
+ 	test_index_replay("delete $i");
+ 	$node_master->psql("postgres", "VACUUM tst;");
+ 	test_index_replay("vacuum $i");
+ 	my ($start, $end) = (100001 + ($i - 1) * 10000, 100000 + $i * 10000);
+ 	$node_master->psql("postgres", "INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series($start,$end) i;");
+ 	test_index_replay("insert $i");
+ }
diff --git a/doc/src/sgml/bloom.sgml b/doc/src/sgml/bloom.sgml
new file mode 100644
index ...c207e6d
*** a/doc/src/sgml/bloom.sgml
--- b/doc/src/sgml/bloom.sgml
***************
*** 0 ****
--- 1,218 ----
+ <!-- doc/src/sgml/bloom.sgml -->
+ 
+ <sect1 id="bloom" xreflabel="bloom">
+  <title>bloom</title>
+ 
+  <indexterm zone="bloom">
+   <primary>bloom</primary>
+  </indexterm>
+ 
+  <para>
+   <literal>bloom</> is a contrib which implements index access method.  It comes
+   as example of custom access methods and generic WAL records usage.  But it
+   is also useful itself.
+  </para>
+ 
+  <sect2>
+   <title>Introduction</title>
+ 
+   <para>
+    Implementation of
+    <ulink url="http://en.wikipedia.org/wiki/Bloom_filter">Bloom filter</ulink>
+    allows fast exclusion of non-candidate tuples.
+    Since signature is a lossy representation of all indexed attributes, 
+    search results should be rechecked using heap information. 
+    User can specify signature length (in uint16, default is 5) and the number of 
+    bits, which can be setted, per attribute (1 < colN < 2048).
+   </para>
+ 
+   <para>
+    This index is useful if table has many attributes and queries can include
+    their arbitary combinations.  Traditional <literal>btree</> index is faster
+    than bloom index, but it'd require too many indexes to support all possible 
+    queries, while one need only one bloom index.  Bloom index supports only 
+    equality comparison.  Since it's a signature file, not a tree, it always
+    should be readed fully, but sequentially, so index search performance is 
+    constant and doesn't depend on a query. 
+   </para>
+  </sect2>
+ 
+  <sect2>
+   <title>Parameters</title>
+ 
+   <para>
+    <literal>bloom</> indexes accept following parameters in <literal>WITH</>
+    clause.
+   </para>
+ 
+    <variablelist>
+    <varlistentry>
+     <term><literal>length</></term>
+     <listitem>
+      <para>
+       Length of signature in uint16 type values
+      </para>
+     </listitem>
+    </varlistentry>
+    </variablelist>
+    <variablelist>
+    <varlistentry>
+     <term><literal>col1 &mdash; col16</></term>
+     <listitem>
+      <para>
+       Number of bits for corresponding column
+      </para>
+     </listitem>
+    </varlistentry>
+    </variablelist>
+  </sect2>
+ 
+  <sect2>
+   <title>Examples</title>
+ 
+   <para>
+    Example of index definition is given below.
+   </para>
+ 
+ <programlisting>
+ CREATE INDEX bloomidx ON tbloom(i1,i2,i3) 
+        WITH (length=5, col1=2, col2=2, col3=4);
+ </programlisting>
+ 
+   <para>
+    Here, we create bloom index with signature length 80 bits and attributes
+    i1, i2  mapped to 2 bits, attribute i3 - to 4 bits.
+   </para>
+ 
+   <para>
+    Example of index definition and usage is given below.
+   </para>
+ 
+ <programlisting>
+ CREATE TABLE tbloom AS
+ SELECT
+     random()::int as i1,
+     random()::int as i2,
+     random()::int as i3,
+     random()::int as i4,
+     random()::int as i5,
+     random()::int as i6,
+     random()::int as i7,
+     random()::int as i8,
+     random()::int as i9,
+     random()::int as i10,
+     random()::int as i11,
+     random()::int as i12,
+     random()::int as i13
+ FROM
+     generate_series(1,1000);
+ CREATE INDEX bloomidx ON tbloom USING
+              bloom (i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12);
+ SELECT pg_relation_size('bloomidx');
+ CREATE index btree_idx ON tbloom(i1,i2,i3,i4,i5,i6,i7,i8,i9,i10,i11,i12);
+ SELECT pg_relation_size('btree_idx');
+ </programlisting>
+ 
+ <programlisting>
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                                    QUERY PLAN
+ -----------------------------------------------------------------------------------------------------------------
+  Bitmap Heap Scan on tbloom  (cost=1.50..5.52 rows=1 width=52) (actual time=0.057..0.057 rows=0 loops=1)
+    Recheck Cond: ((i2 = 20) AND (i10 = 15))
+    ->  Bitmap Index Scan on bloomidx  (cost=0.00..1.50 rows=1 width=0) (actual time=0.041..0.041 rows=9 loops=1)
+          Index Cond: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.081 ms
+ (5 rows)
+ </programlisting>
+ 
+   <para>
+    Seqscan is slow.
+   </para>
+ 
+ <programlisting>
+ =# SET enable_bitmapscan = off;
+ =# SET enable_indexscan = off;
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                             QUERY PLAN
+ --------------------------------------------------------------------------------------------------
+  Seq Scan on tbloom  (cost=0.00..25.00 rows=1 width=52) (actual time=0.162..0.162 rows=0 loops=1)
+    Filter: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.181 ms
+ (3 rows)
+ </programlisting>
+ 
+  <para>
+   Btree index will be not used for this query.
+  </para>
+ 
+ <programlisting>
+ =# DROP INDEX bloomidx;
+ =# CREATE INDEX btree_idx ON tbloom(i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12);
+ =# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 20 AND i10 = 15;
+                                             QUERY PLAN
+ --------------------------------------------------------------------------------------------------
+  Seq Scan on tbloom (cost=0.00..25.00 rows=1 width=52) (actual time=0.210..0.210 rows=0 loops=1)
+    Filter: ((i2 = 20) AND (i10 = 15))
+  Total runtime: 0.250 ms
+ (3 rows)
+ </programlisting>
+  </sect2>
+ 
+  <sect2>
+   <title>Opclass interface</title>
+ 
+   <para>
+    Bloom opclass interface is simple.  It requires 1 supporting function:
+    hash function for indexing datatype.  And it provides 1 search operator:
+    equality operator.  The example below shows <literal>opclass</> definition
+    for <literal>text</> datatype.
+   </para>
+ 
+ <programlisting>
+ CREATE OPERATOR CLASS text_ops
+ DEFAULT FOR TYPE text USING bloom AS
+     OPERATOR    1   =(text, text),
+     FUNCTION    1   hashtext(text);
+ </programlisting>
+  </sect2>
+ 
+  <sect2>
+   <title>Limitation</title>
+   <para>
+ 
+    <itemizedlist>
+     <listitem>
+      <para>
+       For now, only opclasses for <literal>int4</>, <literal>text</> comes
+       with contrib.  However, users may define more of them.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       Only <literal>=</literal> operator is supported for search now.  But it's
+       possible to add support of arrays with contains and intersection
+       operations in future.
+      </para>
+     </listitem>
+    </itemizedlist>
+   </para>
+  </sect2>
+ 
+  <sect2>
+   <title>Authors</title>
+ 
+   <para>
+    Teodor Sigaev <email>teodor@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+ 
+   <para>
+    Alexander Korotkov <email>a.korotkov@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+ 
+   <para>
+    Oleg Bartunov <email>obartunov@postgrespro.ru</email>, Postgres Professional, Moscow, Russia
+   </para>
+  </sect2>
+ 
+ </sect1>
diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
new file mode 100644
index 4e3f337..c8708ec
*** a/doc/src/sgml/contrib.sgml
--- b/doc/src/sgml/contrib.sgml
*************** CREATE EXTENSION <replaceable>module_nam
*** 105,110 ****
--- 105,111 ----
   &adminpack;
   &auth-delay;
   &auto-explain;
+  &bloom;
   &btree-gin;
   &btree-gist;
   &chkpass;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
new file mode 100644
index 9046f50..6c0ad3f
*** a/doc/src/sgml/filelist.sgml
--- b/doc/src/sgml/filelist.sgml
***************
*** 107,112 ****
--- 107,113 ----
  <!ENTITY adminpack       SYSTEM "adminpack.sgml">
  <!ENTITY auth-delay      SYSTEM "auth-delay.sgml">
  <!ENTITY auto-explain    SYSTEM "auto-explain.sgml">
+ <!ENTITY bloom           SYSTEM "bloom.sgml">
  <!ENTITY btree-gin       SYSTEM "btree-gin.sgml">
  <!ENTITY btree-gist      SYSTEM "btree-gist.sgml">
  <!ENTITY chkpass         SYSTEM "chkpass.sgml">
#96Aleksander Alekseev
a.alekseev@postgrespro.ru
In reply to: Alexander Korotkov (#95)
Re: WIP: Access method extendability

Hello

Fixed.

Thanks. I don't have any other suggestions at the moment. Let see whats
committers opinion on this.

--
Best regards,
Aleksander Alekseev
http://eax.me/

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#97Emre Hasegeli
emre@hasegeli.com
In reply to: Aleksander Alekseev (#96)
1 attachment(s)
Re: WIP: Access method extendability

I think the variable "magick" should be "magic". Patch attached.

Attachments:

bloom-magick.patchapplication/octet-stream; name=bloom-magick.patchDownload
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index d5284f3..381d723 100644
--- a/contrib/bloom/bloom.h
+++ b/contrib/bloom/bloom.h
@@ -85,29 +85,29 @@ typedef struct BloomOptions
 typedef BlockNumber FreeBlockNumberArray[
 										 MAXALIGN_DOWN(
 		BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(BloomPageOpaqueData))
 	   - MAXALIGN(sizeof(uint16) * 2 + sizeof(uint32) + sizeof(BloomOptions))
 													   ) / sizeof(BlockNumber)
 ];
 
 /* Metadata of bloom index */
 typedef struct BloomMetaPageData
 {
-	uint32		magickNumber;
+	uint32		magicNumber;
 	uint16		nStart;
 	uint16		nEnd;
 	BloomOptions opts;
 	FreeBlockNumberArray notFullPage;
 }	BloomMetaPageData;
 
-/* Magic number to distinguish bloom pages among anothers */
-#define BLOOM_MAGICK_NUMBER (0xDBAC0DED)
+/* Magic number to distinguish bloom pages among others */
+#define BLOOM_MAGIC_NUMBER (0xDBAC0DED)
 
 /* Number of blocks numbers fit in BloomMetaPageData */
 #define BloomMetaBlockN		(sizeof(FreeBlockNumberArray) / sizeof(BlockNumber))
 
 #define BloomPageGetMeta(page)	((BloomMetaPageData *) PageGetContents(page))
 
 typedef struct BloomState
 {
 	FmgrInfo	hashFn[INDEX_MAX_KEYS];
 	BloomOptions opts;			/* copy of options on index's metapage */
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index be056c3..a09744d 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -138,21 +138,21 @@ initBloomState(BloomState *state, Relation index)
 
 		buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
 		LockBuffer(buffer, BUFFER_LOCK_SHARE);
 
 		page = BufferGetPage(buffer);
 
 		if (!BloomPageIsMeta(page))
 			elog(ERROR, "Relation is not a bloom index");
 		meta = BloomPageGetMeta(BufferGetPage(buffer));
 
-		if (meta->magickNumber != BLOOM_MAGICK_NUMBER)
+		if (meta->magicNumber != BLOOM_MAGIC_NUMBER)
 			elog(ERROR, "Relation is not a bloom index");
 
 		*opts = meta->opts;
 
 		UnlockReleaseBuffer(buffer);
 
 		index->rd_amcache = (void *) opts;
 	}
 
 	memcpy(&state->opts, index->rd_amcache, sizeof(state->opts));
@@ -413,21 +413,21 @@ BloomInitMetapage(Relation index)
 		index->rd_options = palloc0(sizeof(BloomOptions));
 	adjustBloomOptions((BloomOptions *) index->rd_options);
 
 	/* Initialize contents of meta page */
 	state = GenericXLogStart(index);
 	metaPage = GenericXLogRegister(state, metaBuffer, true);
 
 	BloomInitPage(metaPage, BLOOM_META);
 	metadata = BloomPageGetMeta(metaPage);
 	memset(metadata, 0, sizeof(BloomMetaPageData));
-	metadata->magickNumber = BLOOM_MAGICK_NUMBER;
+	metadata->magicNumber = BLOOM_MAGIC_NUMBER;
 	metadata->opts = *((BloomOptions *) index->rd_options);
 	((PageHeader) metaPage)->pd_lower += sizeof(BloomMetaPageData);
 
 	GenericXLogFinish(state);
 	UnlockReleaseBuffer(metaBuffer);
 }
 
 /*
  * Initialize options for bloom index.
  */