�����JFIF��XX����������    $.' ",#(7),01444'9=82<.342  2!!22222222222222222222222222222222222222222222222222�����"����4���������������������������� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������,�PG"Z_�4�˷����kjز�Z�,F+��_z�,�© �����zh6�٨�ic�fu������������������������������������#ډb���_�N��?�����������wQ���5-�~�I���8���������������������������������TK<5o�Iv-������������������k�_U_������������������������������~b�M��d��������Ӝ�U�Hh��?]��E�w��Q���k�{��_}qFW7HTՑ��Y��F�����?_�'ϔ��_�Ջt������������������������=||I �����6�έ"�����D���/[�k�9����Y�8������ds|\���Ҿp6�Ҵ���]��.����6���z<�v��@]�i%������������������������$j��~����g��J>��no����pM[me�i$[�����������s�o�ᘨ�˸ nɜG-�ĨU�ycP���3.DB�li�;���������������������hj���x����7Z^�N�h��������N3u{�:j�����x�힞��#M��&��jL P@��_���� P�������������������&��o8��������9������@Sz���6�t7#O�ߋ �����s}Yf�T������lmr����Z)'N��k�۞p�����w\�T���������������ȯ?�8`���O��i{wﭹW�[�r�� ��Q4F�׊������3m&L�=��h3�������z~��#����\�l :�F,j@�� ʱ�wQT����8�"kJO����6�֚l������������������}����R�>ډK���]��y����&����p�}b������;N�1�m�r$����|��7�>e�@���B�TM*-i�H��g�D�)� E�m�|�ؘbҗ�a���Ҿ����������������t4�����o���G��*oCN�rP���Q��@z,|?W[0���������:�n,j���WiE��W������$~/�hp\��?��{(�0���+�Y8rΟ�+����>S-S���������������VN;���}�s?.����� w��9��˟<���Mq4�Wv'������{)0�1mB����V����W[��������8�/<� �%���wT^�5���b��)iM� p�g�N�&ݝ������������VO~��q���u���9��� ����!��J27�����$����O-���! �:���%H��� ـ�������y�ΠM=t{!S�� �oK8�������t<����è��������:a��������[������ա�H���~��w��Qz`�p����o�^ ������Q��n����� �,uu�C��$ ^���,�������8�#��:�6��e�|~�����������!�3��3.�\0�����q��o�4`.|� ����y�Q�`~;�d�ׯ,��O�Zw�������`73�v�܋�<�����Ȏ�� ـ4k��5�K�a�u�=9Yd��$>x�A�&�� j0� ���vF��� Y���|�y��� ~�6�@c��1vOp��������Ig�����4��l�OD�����L����� R���c���j�_�uX�6��3?nk��Wy�f;^*B� ��@���~a�`��Eu�������+�����6�L��.ü>��}y���}_�O�6�͐�:�Yr���G�X��kG������l^w����������~㒶sy���Iu�!���� W ��X��N�7BV��O��!X�2����wvG�R�f�T#�����t�/?���%8�^�W�aT����G�cL�M���I��(J����1~�8�?aT ���]����AS�E��(��*E}� 2������#I/�׍qz��^t�̔���������b�Yz4x����t�){ OH�����+(E��A&�N�������XT��o��"�XC����'���)}�J�z�p� ����~5�}�^����+�6����w��c��Q�|�Lp�d�H��}�(�.|����k��c4^�����"�����Z?ȕ ��a<�������L�!0�39C� �Eu�����C�F�Ew�ç ;�n?�*o���B�8�bʝ���'#Rqf����M}7����]�������s2tcS{�\icTx;�\��7K���P������ʇ Z O-��~�������c>"��?��������P�����E��O�8��@�8��G��Q�g�a�Վ���󁶠��䧘��_%#r�>�����1�z�a���eb��qcP��ѵ��n���#L��� =��׀t� L�7�`�����V����A{�C:�g���e@�����w1 Xp�3�c3�ġ�������p��M"'-�@n4���fG���B3�DJ�8[Jo�ߐ���gK)ƛ��$���� �������8�3�����+���� �����6�ʻ���� ���S�kI�*KZlT _`�������?��K�����QK�d���������B`�s}�>���`������*�>��,*@J�d�oF*�����弝��O}�k��s��]��y�ߘ�������c1G�V���<=�7��7����6��q�PT��tXԀ�!9*4�4Tހ���3XΛex�46�������Y��D ����� ����BdemDa����\�_l,����G�/���֌7���Y�](�xTt^%�GE�����4�}bT����ڹ�����;��Y)���B�Q��u��>J/J ���⮶.�XԄ��j�ݳ������+E��d ���r�5�_D�����1 ���o�� �B�x�΢�#����<��W�����8���R6�@���g�M�.��� dr�D��>(otU��@�x=��~v���2� ӣ�d�oBd�����3�eO�6�㣷����������ݜ�6��6Y��Qz`����S��{���\P��~z m5{J/L��1������<�e�ͅPu���b�]�ϔ��������'�������f�b� Zpw��c`"��i���BD@:)ִ�:�]��h���v�E��w���T�l�������P����"Ju�}��وV ��J��G6��. J/�Qgl߭�e�����@�z�Zev2u����)]կ���������7x�������s�M�-<ɯ�c��r��v�����@��$�ޮ}lk���a����'����>x��O\�Z������Fu>������ck#��&:��`�$��ai�>2Δ����l���oF[h�������lE�ܺ�Π���k:)���`������� $[6�����9�����kOw�\|�����8}������ބ:��񶐕��������I�A1/���=�2[�,�!��.}gN#�u����b���� ~���������݊��}34q�����d�E��L��������c��$���"�[q�U�硬g^��%B ��z���r�p�������J�ru%v\h�����1Y�ne`������ǥ:g����pQM~�^��Xi� ��`S�:V2������9.�P���V������?B�k�� ��������AEvw%�_�9C�Q����wKekP�ؠ�\������;Io d�{ ߞo�c1eP�����\� `����E=���@K<�Y��������eڼ�J����w����{av�F�'�M�@��������������/J��+9p����|]���������Iw &`���8���&�M�hg���[�{�������Xj���%��Ӓ�������������������$��(�����ʹN�������<>�I���RY�����K2�NPlL�ɀ�)��&e��������B+ь����(������������������� � �JTx����_?EZ� }@���� 6�U���뙢ط�z��dWI��n` D����噥�[��uV��"�G&�����Ú����2�g�}&m���������������������?ċ���"����Om#�������������������������� ��{���������������������ON��"S�X���Ne��ysQ���@�������������Fn��Vg�����dX�~nj����������������������]J�<�K]:����FW���b�������62����������=��5f����JKw����bf�X������������������������55��~J �%^�������:�-�QIE��P��v�nZum� z � ~ə ���� ���ة����;�f��\v�������g�8�1��f2�������������������������4;�V���ǔ�)�������������������9���1\������������������������������c��v�/'Ƞ�w������������������$�4�R-��t����������������������������������� e�6�/�ġ �̕Ecy�J���u�B���<�W�ַ~�w[B1L۲�-JS΂�{���΃�������������������������������������������A��20�c#���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@���� 0!1@AP"#2Q`$3V�%45a6�FRUq����� ������^7ׅ,$n��������+��F�`��2X'��0vM��p�L=�������5��8������u�p~���.�`r�����\����O��,ư�0oS ��_�M�����l���4�kv\JSd���x���SW�<��Ae�IX����������$I���w�:S���y���›R��9�Q[���,�5�;�@]�%���u�@ *ro�lbI �� ��+���%m:�͇ZV�����u�̉����θau<�fc�.����{�4Ա� �Q����*�Sm��8\ujqs]{kN���)qO�y�_*dJ�b�7���yQqI&9�ԌK!�M}�R�;�������S�T���1���i[U�ɵz�]��U)V�S6���3$K{��ߊ<�(� E]Զ[ǼENg�����'�\?#)Dkf��J���o��v���'�%ƞ�&K�u��!��b�35LX�Ϸ��63$K�a�;�9>,R��W��3�3� d�JeTYE.Mϧ��-�o�j3+y��y^�c�������VO�9NV\nd�1 ��!͕_)a�v;����թ�M�lWR1��)El��P;��yوÏ�u 3�k�5Pr6<�⒲l�!˞*��u־�n�!�l:����UNW ��%��Chx8vL'��X�@��*��)���̮��ˍ��� ����D-M�+J�U�kvK����+�x8��cY������?�Ԡ��~3mo��|�u@[XeY�C�\Kp�x8�oC�C�&����N�~3-H���� ��MX�s�u<`���~"WL��$8ξ��3���a�)|:@�m�\���^�`�@ҷ)�5p+��6���p�%i)P M���ngc�����#0Aruz���RL+xSS?���ʮ}()#�t��mˇ!��0}}y����<�e� �-ή�Ԩ��X������ MF���ԙ~l L.3���}�V뽺�v������멬��Nl�)�2����^�Iq��a��M��qG��T�����c3#������3U�Ǎ���}��לS�|qa��ڃ�+���-��2�f����/��bz��ڐ�� �ݼ[2�ç����k�X�2�* �Z�d���J�G����M*9W���s{��w���T��x��y,�in�O�v��]���n����P�$��JB@=4�OTI�n��e�22a\����q�d���%�$��(���:���: /*�K[PR�fr\nڙdN���F�n�$�4��[�� U�zƶ����� �mʋ���,�ao�u 3�z� �x��Kn����\[��VFmbE;�_U��&V�Gg�]L�۪&#n%�$ɯ��dG���D�TI=�%+AB�Ru#��b4�1�»x�cs�YzڙJG��f��Il���d�eF'T� iA��T���uC�$����Y��H?����[!G`}���ͪ� �纤Hv\������j�Ex�K���!���OiƸ�Yj�+u-<���'q����uN�*�r\��+�]���<�wOZ.fp�ێ��,-*)V?j-kÊ#�`�r��dV����(�ݽBk�����G�ƛk�QmUڗe��Z���f}|����8�8��a���i��3'J�����~G_�^���d�8w������ R�`(�~�.��u���l�s+g�bv���W���lGc}��u���afE~1�Ue������Z�0�8�=e�� f@/�jqEKQQ�J���oN��J���W5~M>$6�Lt�;$ʳ{���^��6�{����v6���ķܰg�V�cnn �~z�x�«�,2�u�?cE+Ș�H؎�%�Za�)���X>uW�Tz�Nyo����s���FQƤ��$��*�&�LLXL)�1�" L��eO��ɟ�9=���:t��Z���c��Ž���Y?�ӭV�wv�~,Y��r�ۗ�|�y��GaF�����C�����.�+� ���v1���fήJ�����]�S��T��B��n5sW}y�$��~z�'�c ��8 ��� ,! �p��VN�S��N�N�q��y8z˱�A��4��*��'������2n<�s���^ǧ˭P�Jޮɏ�U�G�L�J�*#��<�V��t7�8����TĜ>��i}K%,���)[��z�21z ?�N�i�n1?T�I�R#��m-�����������������1����lA�`��fT5+��ܐ�c�q՝��ʐ��,���3�f2U�եmab��#ŠdQ�y>\��)�SLY����w#��.���ʑ�f��� ,"+�w�~�N�'�c�O�3F�������N<���)j��&��,-� �љ���֊�_�zS���TǦ����w�>��?�������n��U仆�V���e�����0���$�C�d���rP �m�׈e�Xm�Vu� �L��.�bֹ��� �[Դaզ���*��\y�8�Է:�Ez\�0�Kq�C b��̘��cө���Q��=0Y��s�N��S.����3.���O�o:���#���v7�[#߫ ��5�܎�L���Er4���9n��COWlG�^��0k�%<���ZB���aB_���������'=��{i�v�l�$�uC���mƎҝ{�c㱼�y]���W�i ��ߧc��m�H� m�"�"�����;Y�ߝ�Z�Ǔ�����:S#��|}�y�,/k�Ld� TA�(�AI$+I3��;Y*���Z��}|��ӧO��d�v��..#:n��f>�>���ȶI�TX��� 8��y����"d�R�|�)0���=���n4��6ⲑ�+��r<�O�܂~zh�z����7ܓ�HH�Ga롏���nCo�>������a ���~]���R���̲c?�6(�q�;5%� |�uj�~z8R�=X��I�V=�|{v�Gj\gc��q����z�؋%M�ߍ����1y��#��@f^���^�>N������#x#۹��6�Y~�?�dfPO��{��P�4��V��u1E1J �*|���%����JN��`eWu�zk M6���q t[�� ��g�G���v��WIG��u_ft����5�j�"�Y�:T��ɐ���*�;� e5���4����q$C��2d�}���� _S�L#m�Yp��O�.�C�;��c����Hi#֩%+) �Ӎ��ƲV���SYź��g |���tj��3�8���r|���V��1#;.SQ�A[���S������#���`n�+���$��$�I �P\[�@�s��(�ED�z���P��])8�G#��0B��[ى��X�II�q<��9�~[Z멜�Z�⊔IWU&A>�P~�#��dp<�?����7���c��'~���5 ��+$���lx@�M�dm��n<=e�dyX��?{�|Aef ,|n3�<~z�ƃ�uۧ�����P��Y,�ӥQ�*g�#먙R�\���;T��i,��[9Qi歉����c>]9�� ��"�c��P�� �Md?٥��If�ت�u��k��/����F��9�c*9��Ǎ:�ØF���z�n*�@|I�ށ9����N3{'��[�'ͬ�Ҳ4��#}��!�V� Fu��,�,mTIk���v C�7v���B�6k�T9��1�*l� '~��ƞF��lU��'�M ����][ΩũJ_�{�i�I�n��$����L�� j��O�dx�����kza۪��#�E��Cl����x˘�o�����V���ɞ�ljr��)�/,�߬h�L��#��^��L�ф�,íMƁe�̩�NB�L�����iL����q�}��(��q��6IçJ$�W�E$��:������=#����(�K�B����zђ <��K(�N�۫K�w��^O{!����)��H���>x�������lx�?>Պ�+�>�W���,Ly!_�D���Ō�l���Q�!�[ �S����J��1��Ɛ�Y}��b,+�Lo�x�ɓ)����=�y�oh�@�꥟/��I��ѭ=��P�y9��� �ۍYӘ�e+�p�Jnϱ?V\SO%�(�t� ���=?MR�[Ș�����d�/ ��n�l��B�7j� ��!�;ӥ�/�[-���A�>��dN�sLj ��,ɪv��=1c�.SQ�O3�U���ƀ�ܽ�E����������̻��9G�ϷD�7(�}��Ävӌ\��y�_0[w ���<΍>����a_��[0+�L��F.�޺��f�>oN�T����q;���y\��bՃ��y�jH�<|q-eɏ�_?_9+P���Hp$�����[ux�K w�Mw��N�ی'$Y2�=��q���KB��P��~�������Yul:�[<����F1�2�O���5=d����]Y�sw:���Ϯ���E��j,_Q��X��z`H1,#II ��d�wr��P˂@�ZJV����y$�\y�{}��^~���[:N����ߌ�U�������O��d�����ؾe��${p>G��3c���Ė�lʌ�� ת��[��`ϱ�-W����dg�I��ig2��� ��}s ��ؤ(%#sS@���~���3�X�nRG�~\jc3�v��ӍL��M[JB�T��s3}��j�Nʖ��W����;7���ç?=X�F=-�=����q�ߚ���#���='�c��7���ڑW�I(O+=:uxq�������������e2�zi+�kuG�R��������0�&e�n���iT^J����~\jy���p'dtG��s����O��3����9* �b#Ɋ�� p������[Bws�T�>d4�ۧs���nv�n���U���_�~,�v����ƜJ1��s�� �QIz���)�(lv8M���U=�;����56��G���s#�K���MP�=��LvyGd��}�VwWBF�'�à �?MH�U�g2�� ����!�p�7Q��j��ڴ����=��j�u��� Jn�A s���uM������e��Ɔ�Ҕ�!)�'��8Ϣ�ٔ���ޝ(��Vp���צ֖d=�IC�J�Ǡ{q������kԭ�߸���i��@K����u�|�p=..�*+����x�����z[Aqġ#s2a�Ɗ���RR�)*HRsi�~�a &f��M��P����-K�L@��Z��Xy�'x�{}��Zm+���:�)�) IJ�-i�u���� ���ܒH��'��L(7�y�GӜq���� j��� 6ߌg1�g�o���,kر���tY�?W,���p���e���f�OQS��!K�۟cҒA�|ս�j�>��=⬒��˧L[�� �߿2JaB~R��u�:��Q�] �0H~���]�7��Ƽ�I���(�}��cq '�ήET���q�?f�ab���ӥvr� �)o��-Q��_'����ᴎo��K������;��V���o��%���~OK ����*��b�f:���-ťIR��`B�5!RB@���ï�� �u �̯e\�_U�_������� g�ES��3��������QT��a�����x����U<~�c?�*�#]�MW,[8O�a�x��]�1bC|踤�P��lw5V%�)�{t�<��d��5���0i�XSU��m:��Z�┵�i�"��1�^B�-��P�hJ��&)O��*�D��c�W��vM��)����}���P��ܗ-q����\mmζZ-l@�}��a��E�6��F�@��&Sg@���ݚ�M����� ȹ 4����#p�\H����dYDo�H���"��\��..R�B�H�z_�/5˘����6��KhJR��P�mƶi�m���3��,#c�co��q�a)*P�t����R�m�k�7x�D�E�\Y�閣_X�<���~�)���c[[�BP����6�Yq���S��0����%_����;��Àv�~�| VS؇ ��'O0��F0��\���U�-�d@�����7�SJ*z��3n��y��P����O����������m�~�P�3|Y��ʉr#�C�<�G~�.,! ���bqx���h~0=��!ǫ�jy����l��O,�[B��~��|9��ٱ����Xly�#�i�B��g%�S��������tˋ���e���ې��\[d�t)��.+u�|1 ������#�~Oj����hS�%��i.�~X���I�H�m��0n���c�1uE�q��cF�RF�o���7� �O�ꮧ� ���ۛ{��ʛi5�rw?׌#Qn�TW��~?y$��m\�\o����%W� ?=>S�N@�� �Ʈ���R����N�)�r"C�:��:����� �����#��qb��Y�. �6[��2K����2u�Ǧ�HYR��Q�MV��� �G�$��Q+.>�����nNH��q�^��� ����q��mM��V��D�+�-�#*�U�̒ ���p욳��u:�������IB���m����PV@O���r[b= �� ��1U�E��_Nm�yKbN�O���U�}�the�`�|6֮P>�\2�P�V���I�D�i�P�O;�9�r�mAHG�W�S]��J*�_�G��+kP�2����Ka�Z���H�'K�x�W�MZ%�O�YD�Rc+o��?�q��Ghm��d�S�oh�\�D�|:W������UA�Qc yT�q��������~^�H��/��#p�CZ���T�I�1�ӏT����4��"�ČZ�����}��`w�#�*,ʹ�� ��0�i��課�Om�*�da��^gJ݅{���l�e9uF#T�ֲ��̲�ٞC"�q���ߍ ոޑ�o#�XZTp����@ o�8��(jd��xw�]�,f���`~��|,s��^����f�1���t��|��m�򸄭/ctr��5s��7�9Q�4�H1꠲BB@�l9@���C�����+�wp�xu�£Yc�9��?`@#�o�mH�s2��)�=��2�.�l����jg�9$�Y�S�%*L������R�Y������7Z���,*=�䷘$�������arm�o�ϰ���UW.|�r�uf����IGw�t����Zwo��~5 ��YյhO+=8fF�)�W�7�L9lM�̘·Y���֘YLf�큹�pRF���99.A �"wz��=E\Z���'a� 2��Ǚ�#;�'}�G���*��l��^"q��+2FQ� hj��kŦ��${���ޮ-�T�٭cf�|�3#~�RJ����t��$b�(R��(����r���dx� >U b�&9,>���%E\� Ά�e�$��'�q't��*�א���ެ�b��-|d���SB�O�O��$�R+�H�)�܎�K��1m`;�J�2�Y~9��O�g8=vqD`K[�F)k�[���1m޼c��n���]s�k�z$@��)!I �x՝"v��9=�ZA=`Ɠi �:�E��)`�7��vI��}d�YI�_ �o�:ob���o ���3Q��&D&�2=�� �Ά��;>�h����y.*ⅥS������Ӭ�+q&����j|UƧ�����}���J0��WW< ۋS�)jQR�j���Ư��rN)�Gű�4Ѷ(�S)Ǣ�8��i��W52���No˓� ۍ%�5brOn�L�;�n��\G����=�^U�dI���8$�&���h��'���+�(������cȁ߫k�l��S^���cƗjԌE�ꭔ��gF���Ȓ��@���}O���*;e�v�WV���YJ\�]X'5��ղ�k�F��b 6R�o՜m��i N�i�����>J����?��lPm�U��}>_Z&�KK��q�r��I�D�Չ~�q�3fL�:S�e>���E���-G���{L�6p�e,8��������QI��h��a�Xa��U�A'���ʂ���s�+טIjP�-��y�8ۈZ?J$��W�P� ��R�s�]��|�l(�ԓ��sƊi��o(��S0���Y� 8�T97.�����WiL��c�~�dxc�E|�2!�X�K�Ƙਫ਼�$((�6�~|d9u+�qd�^3�89��Y�6L�.I�����?���iI�q���9�)O/뚅����O���X��X�V��ZF[�یgQ�L��K1���RҖr@v�#��X�l��F���Нy�S�8�7�kF!A��sM���^rkp�jP�DyS$N���q���nxҍ!U�f�!eh�i�2�m����`�Y�I�9r�6� �TF���C}/�y�^���Η���5d�'��9A-��J��>{�_l+�`��A���[�'��յ�ϛ#w:݅�%��X�}�&�PSt�Q�"�-��\縵�/����$Ɨh�Xb�*�y��BS����;W�ջ_mc�����vt?2}1�;qS�d�d~u:2k5�2�R�~�z+|HE!)�Ǟl��7`��0�<�,�2*���Hl-��x�^����'_TV�gZA�'j� ^�2Ϊ��N7t�����?w�� �x1��f��Iz�C-Ȗ��K�^q�;���-W�DvT�7��8�Z�������� hK�(P:��Q- �8�n�Z���܃e貾�<�1�YT<�,�����"�6{�/ �?�͟��|1�:�#g��W�>$����d��J��d�B���=��jf[��%rE^��il:��B���x���Sּ�1հ��,�=��*�7 fcG��#q� �eh?��2�7�����,�!7x��6�n�LC�4x��},Geǝ�tC.��vS �F�43��zz\��;QYC,6����~;RYS/6���|2���5���v��T��i����������mlv��������&� �nRh^ejR�LG�f���? �ۉҬܦƩ��|��Ȱ����>3����!v��i�ʯ�>�v��オ�X3e���_1z�Kȗ\<������!�8���V��]��?b�k41�Re��T�q��mz��TiOʦ�Z��Xq���L������q"+���2ۨ��8}�&N7XU7Ap�d�X��~�׿��&4e�o�F��� �H�����O���č�c�� 懴�6���͉��+)��v;j��ݷ�� �UV�� i��� j���Y9GdÒJ1��詞�����V?h��l�����l�cGs�ځ�������y�Ac������\V3�? �� ܙg�>qH�S,�E�W�[�㺨�uch�⍸�O�}���a��>�q�6�n6�����N6�q��������N� ���! 1AQaq�0@����"2BRb�#Pr���3C`��Scst���$4D���%Td���� ?�����N����a��3��m���C���w��������xA�m�q�m����m������$����4n淿t'��C"w��zU=D�\R+w�p+Y�T�&�պ@��ƃ��3ޯ?�Aﶂ��aŘ���@-�����Q�=���9D��ռ�ѻ@��M�V��P��܅�G5�f�Y<�u=,EC)�<�Fy'�"�&�չ�X~f��l�KԆV��?�� �W�N����=(� �;���{�r����ٌ�Y���h{�١������jW����P���Tc�����X�K�r��}���w�R��%��?���E��m�� �Y�q|����\lEE4����r���}�lsI�Y������f�$�=�d�yO����p�����yBj8jU�o�/�S��?�U��*������ˍ�0�������u�q�m [�?f����a�� )Q�>����6#������� ?����0UQ����,IX���(6ڵ[�DI�MNލ�c&���υ�j\��X�R|,4��� j������T�hA�e��^���d���b<����n�� �즇�=!���3�^�`j�h�ȓr��jẕ�c�,ٞX����-����a�ﶔ���#�$��]w�O��Ӫ�1y%��L�Y<�wg#�ǝ�̗`�x�xa�t�w��»1���o7o5��>�m뭛C���Uƃߜ}�C���y1Xνm�F8�jI���]����H���ۺиE@I�i;r�8ӭ�����V�F�Շ| ��&?�3|x�B�MuS�Ge�=Ӕ�#BE5G������Y!z��_e��q�р/W>|-�Ci߇�t�1ޯќd�R3�u��g�=0 5��[?�#͏��q�cf���H��{ ?u�=?�?ǯ���}Z��z���hmΔ�BFTW�����<�q��(v� ��!��z���iW]*�J�V�z��gX֧A�q�&��/w���u�gYӘa���; �i=����g:��?2�dž6�ى�k�4�>�Pxs����}������G�9���3 ���)gG�R<>r h�$��'nc�h�P��Bj��J�ҧH� -��N1���N��?��~��}-q!=��_2hc�M��l�vY%UE�@|�v����M2�.Y[|y�"Eï��K�ZF,�ɯ?,q�?v�M 80jx�"�;�9vk�����+ ֧�� �ȺU��?�%�vcV��mA�6��Qg^M�����A}�3�nl� QRN�l8�kkn�'�����(��M�7m9و�q���%ޟ���*h$Zk"��$�9��: �?U8�Sl��,,|ɒ��xH(ѷ����Gn�/Q�4�P��G�%��Ա8�N��!� �&�7�;���eKM7�4��9R/%����l�c>�x;������>��C�:�����t��h?aKX�bhe�ᜋ^�$�Iհ �hr7%F$�E��Fd���t��5���+�(M6�t����Ü�UU|zW�=a�Ts�Tg������dqP�Q����b'�m���1{|Y����X�N��b �P~��F^F:����k6�"�j!�� �I�r�`��1&�-$�Bevk:y���#y�w��I0��x��=D�4��tU���P�ZH��ڠ底taP��6����b>�xa�����Q�#� WeF��ŮNj�p�J* mQ�N�����*I�-*�ȩ�F�g�3 �5��V�ʊ�ɮ�a��5F���O@{���NX��?����H�]3��1�Ri_u��������ѕ�� ����0��� F��~��:60�p�͈�S��qX#a�5>���`�o&+�<2�D����: �������ڝ�$�nP���*)�N�|y�Ej�F�5ټ�e���ihy�Z �>���k�bH�a�v��h�-#���!�Po=@k̆IEN��@��}Ll?j�O������߭�ʞ���Q|A07x���wt!xf���I2?Z��<ץ�T���cU�j��]���陎Ltl �}5�ϓ��$�,��O�mˊ�;�@O��jE��j(�ا,��LX���LO���Ц�90�O �.����a��nA���7������j4 ��W��_ٓ���zW�jcB������y՗+EM�)d���N�g6�y1_x��p�$Lv�:��9�"z��p���ʙ$��^��JԼ*�ϭ����o���=x�Lj�6�J��u82�A�H�3$�ٕ@�=Vv�]�'�qEz�;I˼��)��=��ɯ���x �/�W(V���p�����$ �m�������u�����񶤑Oqˎ�T����r��㠚x�sr�GC��byp�G��1ߠ�w e�8�$⿄����/�M{*}��W�]˷.�CK\�ުx���/$�WP�w���r� |i���&�}�{�X� �>��$-��l���?-z���g����lΆ���(F���h�vS*���b���߲ڡn,|)mrH[���a�3�ר�[1��3o_�U�3�TC�$��(�=�)0�kgP���� ��u�^=��4 �WYCҸ:��vQ�ר�X�à��tk�m,�t*��^�,�}D*�� �"(�I��9R����>`�`��[~Q]�#af��i6l��8���6�:,s�s�N6�j"�A4���IuQ��6E,�GnH��zS�HO�uk�5$�I�4��ؤ�Q9�@��C����wp��BGv[]�u�Ov����0I4���\��y�����Q�Ѹ��~>Z��8�T��a��q�ޣ;z��a���/��S��I:�ܫ_�|������>=Z����8:�S��U�I�J��"IY���8%b8���H��:�QO�6�;7�I�S��J��ҌAά3��>c���E+&jf$eC+�z�;��V����� �r���ʺ������my�e���aQ�f&��6�ND���.:��NT�vm�<- u���ǝ\MvZY�N�NT��-A�>jr!S��n�O 1�3�Ns�%�3D@���`������ܟ 1�^c<���� �a�ɽ�̲�Xë#�w�|y�cW�=�9I*H8�p�^(4���՗�k��arOcW�tO�\�ƍR��8����'�K���I�Q�����?5�>[�}��yU�ײ -h��=��% q�ThG�2�)���"ו3]�!kB��*p�FDl�A���,�eEi�H�f�Ps�����5�H:�Փ~�H�0Dت�D�I����h�F3�������c��2���E��9�H��5�zԑ�ʚ�i�X�=:m�xg�hd(�v����׊�9iS��O��d@0ڽ���:�p�5�h-��t�&���X�q�ӕ,��ie�|���7A�2���O%P��E��htj��Y1��w�Ѓ!����  ���� ࢽ��My�7�\�a�@�ţ�J ��4�Ȼ�F�@o�̒?4�wx��)��]�P��~�����u�����5�����7X ��9��^ܩ�U;Iꭆ 5 �������eK2�7(�{|��Y׎ �V��\"���Z�1� Z�����}��(�Ǝ"�1S���_�vE30>���p;� ΝD��%x�W�?W?v����o�^V�i�d��r[��/&>�~`�9Wh��y�;���R���� ;;ɮT��?����r$�g1�K����A��C��c��K��l:�'��3 c�ﳯ*"t8�~l��)���m��+U,z��`(��>yJ�?����h>��]��v��ЍG*�{`��;y]��I�T� ;c��NU�fo¾h���/$���|NS���1�S�"�H��V���T���4��uhǜ�]�v;���5�͠x��'C\�SBpl���h}�N����� A�Bx���%��ޭ�l��/����T��w�ʽ]D�=����K���ž�r㻠l4�S�O?=�k �M:� ��c�C�a�#ha���)�ѐxc�s���gP�iG���{+���x���Q���I= �� z��ԫ+ �8"�k�ñ�j=|����c ��y��CF��/���*9ж�h{ �?4�o� ��k�m�Q�N�x��;�Y��4膚�a�w?�6�>�e]�����Q�r�:����g�,i"�����ԩA��*M�<�G��b�if��l^M��5�� �Ҩ�{����6J��ZJ�����P�*�����Y���ݛu�_4�9�I8�7���������,^ToR���m4�H��?�N�S�ѕw��/S��甍�@�9H�S�T��t�ƻ���ʒU��*{Xs�@����f������֒Li�K{H�w^���������Ϥm�tq���s� ���ք��f:��o~s��g�r��ט� �S�ѱC�e]�x���a��) ���(b-$(�j>�7q�B?ӕ�F��hV25r[7 Y� }L�R��}����*sg+��x�r�2�U=�*'WS��ZDW]�WǞ�<��叓���{�$�9Ou4��y�90-�1�'*D`�c�^o?(�9��u���ݐ��'PI&� f�Jݮ�������:wS����jfP1F:X �H�9dԯ����˝[�_54 �}*;@�ܨ�� ð�yn�T���?�ןd�#���4rG�ͨ��H�1�|-#���Mr�S3��G�3�����)�.᧏3v�z֑��r����$G"�`j �1t��x0<Ɔ�Wh6�y�6��,œ�Ga��gA����y��b��)���h�D��ß�_�m��ü �gG;��e�v��ݝ�nQ� ��C����-�*��o���y�a��M��I�>�<���]obD��"�:���G�A��-\%LT�8���c�)��+y76���o�Q�#*{�(F�⽕�y����=���rW�\p���۩�c���A���^e6��K������ʐ�cVf5$�'->���ՉN"���F�"�UQ@�f��Gb~��#�&�M=��8�ט�JNu9��D��[̤�s�o�~������� G��9T�tW^g5y$b��Y'��س�Ǵ�=��U-2 #�MC�t(�i� �lj�@Q 5�̣i�*�O����s�x�K�f��}\��M{E�V�{�υ��Ƈ�����);�H����I��fe�Lȣr�2��>��W��I�Ȃ6������i��k�� �5�YOxȺ����>��Y�f5'��|��H+��98pj�n�.O�y�������jY��~��i�w'������l�;�s�2��Y��:'lg�ꥴ)o#'Sa�a�K��Z� �m��}�`169�n���"���x��I ��*+� }F<��cГ���F�P�������ֹ*�PqX�x۩��,� ��N�� �4<-����%����:��7����W���u�`����� $�?�I��&����o��o��`v�>��P��"��l���4��5'�Z�gE���8���?��[�X�7(��.Q�-��*���ތL@̲����v��.5���[��=�t\+�CNܛ��,g�SQnH����}*F�G16���&:�t��4ُ"A��̣��$�b �|����#rs��a�����T�� ]�<�j��B�S�('$�ɻ� �wP;�/�n��?�ݜ��x�F��yUn�~mL*-�������Xf�wd^�a�}��f�,=t�׵i�.2/wpN�Ep8�OР���•��R�FJ� 55TZ��T �ɭ�<��]��/�0�r�@�f��V��V����Nz�G��^���7hZi����k��3�,kN�e|�vg�1{9]_i��X5y7� 8e]�U����'�-2,���e"����]ot�I��Y_��n�(JҼ��1�O ]bXc���Nu�No��pS���Q_���_�?i�~�x h5d'�(qw52] ��'ޤ�q��o1�R!���`ywy�A4u���h<קy���\[~�4�\ X�Wt/� 6�����n�F�a8��f���z �3$�t(���q��q�x��^�XWeN'p<-v�!�{�(>ӽDP7��ո0�y)�e$ٕv�Ih'Q�EA�m*�H��RI��=:��� ���4牢) �%_iN�ݧ�l]� �Nt���G��H�L��� ɱ�g<���1V�,�J~�ٹ�"K��Q�� 9�HS�9�?@��k����r�;we݁�]I�!{ �@�G�[�"��`���J:�n]�{�cA�E����V��ʆ���#��U9�6����j�#Y�m\��q�e4h�B�7��C�������d<�?J����1g:ٳ���=Y���D�p�ц� ׈ǔ��1�]26؜oS�'��9�V�FVu�P�h�9�xc�oq�X��p�o�5��Ա5$�9W�V(�[Ak�aY錎qf;�'�[�|���b�6�Ck��)��#a#a˙��8���=äh�4��2��C��4tm^ �n'c����]GQ$[Wҿ��i���vN�{Fu ��1�gx��1┷���N�m��{j-,��x�� Ūm�ЧS�[�s���Gna���䑴�� x�p 8<������97�Q���ϴ�v�aϚG��Rt�Һ׈�f^\r��WH�JU�7Z���y)�vg=����n��4�_)y��D'y�6�]�c�5̪��\� �PF�k����&�c;��cq�$~T�7j ���nç]�<�g ":�to�t}�159�<�/�8������m�b�K#g'I'.W������6��I/��>v��\�MN��g���m�A�yQL�4u�Lj�j9��#44�t��l^�}L����n��R��!��t��±]��r��h6ٍ>�yҏ�N��fU�� ���� Fm@�8}�/u��jb9������he:A�y�ծw��GpΧh�5����l}�3p468��)U��d��c����;Us/�֔�YX�1�O2��uq�s��`hwg�r~�{ R��mhN��؎*q 42�*th��>�#���E����#��Hv�O����q�}������6�e��\�,Wk�#���X��b>��p}�դ��3���T5��†��6��[��@��P�y*n��|'f�֧>�lư΂�̺����SU�'*�q�p�_S�����M�� '��c�6������m�� ySʨ;M��r���Ƌ�m�Kxo,���Gm�P��A�G�:��i��w�9�}M(�^�V��$ǒ�ѽ�9���|���� �a����J�SQ�a���r�B;����}���ٻ֢�2�%U���c�#�g���N�a�ݕ�'�v�[�OY'��3L�3�;,p�]@�S��{ls��X�'���c�jw��k'a�.��}�}&�� �dP�*�bK=ɍ!����;3n�gΊU�ߴmt�'*{,=SzfD� A��ko~�G�aoq�_mi}#�m�������P�Xhύ�����mxǍ�΂���巿zf��Q���c���|kc�����?���W��Y�$���_Lv����l߶��c���`?����l�j�ݲˏ!V��6����U�Ђ(A���4y)H���p�Z_�x��>���e���R��$�/�`^'3qˏ�-&Q�=?��CFVR �D�fV�9��{�8g�������n�h�(P"��6�[�D���< E�����~0<@�`�G�6����Hг�cc�� �c�K.5��D��d�B���`?�XQ��2��ٿyqo&+�1^� DW�0�ꊩ���G�#��Q�nL3��c���������/��x ��1�1�[y�x�პCW��C�c�UĨ80�m�e�4.{�m��u���I=��f�����0QRls9���f���������9���~f�����Ǩ��a�"@�8���ȁ�Q����#c�ic������G��$���G���r/$W�(��W���V�"��m�7�[m�A�m����bo��D� j����۳� l���^�k�h׽����� ��#� iXn�v��eT�k�a�^Y�4�BN���ĕ���0������� !01@Q"2AaPq3BR�������?�����@4�Q�����T3,���㺠�W�[=JK�Ϟ���2�r^7��vc�:�9 �E�ߴ�w�S#d���Ix��u��:��Hp��9E!�� V 2;73|F��9Y���*ʬ�F��D����u&���y؟��^EA��A��(ɩ���^��GV:ݜDy�`��Jr29ܾ�㝉��[���E;Fzx��YG��U�e�Y�C���� ����v-tx����I�sם�Ę�q��Eb�+P\ :>�i�C'�;�����k|z�رn�y]�#ǿb��Q��������w�����(�r|ӹs��[�D��2v-%��@;�8<a���[\o[ϧw��I!��*0�krs)�[�J9^��ʜ��p1)� "��/_>��o��<1����A�E�y^�C��`�x1'ܣn�p��s`l���fQ��):�l����b>�Me�jH^?�kl3(�z:���1ŠK&?Q�~�{�ٺ�h�y���/�[��V�|6��}�KbX����mn[-��7�5q�94�������dm���c^���h� X��5��<�eޘ>G���-�}�دB�ޟ� ��|�rt�M��V+�]�c?�-#ڛ��^ǂ}���Lkr���O��u�>�-D�ry� D?:ޞ�U��ǜ�7�V��?瓮�"�#���r��չģVR;�n���/_� ؉v�ݶe5d�b9��/O��009�G���5n�W����JpA�*�r9�>�1��.[t���s�F���nQ� V 77R�]�ɫ8����_0<՜�IF�u(v��4��F�k�3��E)��N:��yڮe��P�`�1}�$WS��J�SQ�N�j��ٺ��޵�#l���ј(�5=��5�lǏmoW�v-�1����v,W�mn��߀$x�<����v�j(����c]��@#��1������Ǔ���o'��u+����;G�#�޸��v-lη��/(`i⣍Pm^����ԯ̾9Z��F��������n��1��� ��]�[��)�'�������:�֪�W��FC����� �B9،!?���]��V��A�Վ�M��b�w��G F>_DȬ0¤�#�QR�[V��kz���m�w�"��9ZG�7'[��=�Q����j8R?�zf�\a�=��O�U����*oB�A�|G���2�54 �p��.w7� �� ���&������ξxGHp� B%��$g�����t�Џ򤵍z���HN�u�Я�-�'4��0���;_���3������� !01"@AQa2Pq#3BR�������?����ʩca��en��^��8���<�u#��m*08r��y�N"�<�Ѳ0��@\�p��� �����Kv�D��J8�Fҽ� �f�Y��-m�ybX�NP����}�!*8t(�OqѢ��Q�wW�K��ZD��Δ^e��!� ��B�K��p~�����e*l}z#9ң�k���q#�Ft�o��S�R����-�w�!�S���Ӥß|M�l޶V��!eˈ�8Y���c�ЮM2��tk���� ������J�fS����Ö*i/2�����n]�k�\���|4yX�8��U�P.���Ы[���l��@"�t�<������5�lF���vU�����W��W��;�b�cД^6[#7@vU�xgZv��F�6��Q,K�v��� �+Ъ��n��Ǣ��Ft���8��0��c�@�!�Zq s�v�t�;#](B��-�nῃ~���3g������5�J�%���O������n�kB�ĺ�.r��+���#�N$?�q�/�s�6��p��a����a��J/��M�8��6�ܰ"�*������ɗud"\w���aT(����[��F��U՛����RT�b���n�*��6���O��SJ�.�ij<�v�MT��R\c��5l�sZB>F��<7�;EA��{��E���Ö��1U/�#��d1�a�n.1ě����0�ʾR�h��|�R��Ao�3�m3 ��%�� ���28Q�� ��y��φ���H�To�7�lW>����#i`�q���c����a��� �m,B�-j����݋�'mR1Ήt�>��V��p���s�0IbI�C.���1R�ea�����]H�6�����������4B>��o��](��$B���m�����a�!=���?�B� K�Ǿ+�Ծ"�n���K��*��+��[T#�{�E�J�S����Q�����s�5�:�U�\wĐ�f�3����܆&�)�����I���Ԇw��E T�lrTf6Q|R�h:��[K�� �z��c֧�G�C��%\��_�a��84��HcO�bi��ؖV��7H �)*ģK~Xhչ0��4?�0��� �E<���}3���#���u�?�� ��|g�S�6ꊤ�|�I#Hڛ� �ա��w�X��9��7���Ŀ%�SL��y6č��|�F�a 8���b���$�sק�h���b9RAu7�˨p�Č�_\*w��묦��F ����4D~�f����|(�"m���NK��i�S�>�$d7SlA��/�²����SL��|6N�}���S�˯���g��]6��; �#�.��<���q'Q�1|KQ$�����񛩶"�$r�b:���N8�w@��8$�� �AjfG|~�9F ���Y��ʺ��Bwؒ������M:I岎�G��`s�YV5����6��A �b:�W���G�q%l�����F��H���7�������Fsv7���k�� 403WebShell
403Webshell
Server IP : 88.222.222.71  /  Your IP : 216.73.216.150
Web Server : LiteSpeed
System : Linux in-mum-web1874.main-hosting.eu 5.14.0-570.21.1.el9_6.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Jun 11 07:22:35 EDT 2025 x86_64
User : u862839997 ( 862839997)
PHP Version : 8.2.30
Disable Function : system, exec, shell_exec, passthru, mysql_list_dbs, ini_alter, dl, symlink, link, chgrp, leak, popen, apache_child_terminate, virtual, mb_send_mail
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : OFF  |  Python : OFF  |  Sudo : OFF  |  Pkexec : OFF
Directory :  /proc/4022542/root/proc/self/root/opt/go/pkg/mod/golang.org/x/net@v0.33.0/quic/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /proc/4022542/root/proc/self/root/opt/go/pkg/mod/golang.org/x/net@v0.33.0/quic/loss_test.go
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build go1.21

package quic

import (
	"fmt"
	"testing"
	"time"
)

func TestLossAntiAmplificationLimit(t *testing.T) {
	test := newLossTest(t, serverSide, lossTestOpts{})
	test.datagramReceived(1200)
	t.Logf("# consume anti-amplification capacity in a mix of packets")
	test.send(initialSpace, 0, sentPacket{
		size:         1200,
		ackEliciting: true,
		inFlight:     true,
	})
	test.send(initialSpace, 1, sentPacket{
		size:         1200,
		ackEliciting: false,
		inFlight:     false,
	})
	test.send(initialSpace, 2, sentPacket{
		size:         1200,
		ackEliciting: false,
		inFlight:     true,
	})
	t.Logf("# send blocked by anti-amplification limit")
	test.wantSendLimit(ccBlocked)

	t.Logf("# receiving a datagram unblocks server")
	test.datagramReceived(100)
	test.wantSendLimit(ccOK)

	t.Logf("# validating client address removes anti-amplification limit")
	test.validateClientAddress()
	test.wantSendLimit(ccOK)
}

func TestLossRTTSampleNotGenerated(t *testing.T) {
	test := newLossTest(t, clientSide, lossTestOpts{})
	test.send(initialSpace, 0, 1)
	test.send(initialSpace, 2, sentPacket{
		ackEliciting: false,
		inFlight:     false,
	})
	test.advance(10 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{1, 2})
	test.wantAck(initialSpace, 1)
	test.wantVar("latest_rtt", 10*time.Millisecond)
	t.Logf("# smoothed_rtt = latest_rtt")
	test.wantVar("smoothed_rtt", 10*time.Millisecond)
	t.Logf("# rttvar = latest_rtt / 2")
	test.wantVar("rttvar", 5*time.Millisecond)

	// "...an ACK frame SHOULD NOT be used to update RTT estimates if
	// it does not newly acknowledge the largest acknowledged packet."
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-5.1-6
	t.Logf("# acks for older packets do not generate an RTT sample")
	test.advance(1 * time.Millisecond)
	test.ack(initialSpace, 1*time.Millisecond, i64range[packetNumber]{0, 2})
	test.wantAck(initialSpace, 0)
	test.wantVar("smoothed_rtt", 10*time.Millisecond)

	// "An RTT sample MUST NOT be generated on receiving an ACK frame
	// that does not newly acknowledge at least one ack-eliciting packet."
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-5.1-7
	t.Logf("# acks for non-ack-eliciting packets do not generate an RTT sample")
	test.advance(1 * time.Millisecond)
	test.ack(initialSpace, 1*time.Millisecond, i64range[packetNumber]{0, 3})
	test.wantAck(initialSpace, 2)
	test.wantVar("smoothed_rtt", 10*time.Millisecond)
}

func TestLossMinRTT(t *testing.T) {
	test := newLossTest(t, clientSide, lossTestOpts{})

	// "min_rtt MUST be set to the latest_rtt on the first RTT sample."
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-5.2-2
	t.Logf("# min_rtt set on first sample")
	test.send(initialSpace, 0)
	test.advance(10 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(initialSpace, 0)
	test.wantVar("min_rtt", 10*time.Millisecond)

	// "min_rtt MUST be set to the lesser of min_rtt and latest_rtt [...]
	// on all other samples."
	t.Logf("# min_rtt does not increase")
	test.send(initialSpace, 1)
	test.advance(20 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 2})
	test.wantAck(initialSpace, 1)
	test.wantVar("min_rtt", 10*time.Millisecond)

	t.Logf("# min_rtt decreases")
	test.send(initialSpace, 2)
	test.advance(5 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 3})
	test.wantAck(initialSpace, 2)
	test.wantVar("min_rtt", 5*time.Millisecond)
}

func TestLossMinRTTAfterCongestion(t *testing.T) {
	// "Endpoints SHOULD set the min_rtt to the newest RTT sample
	// after persistent congestion is established."
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-5.2-5
	test := newLossTest(t, clientSide, lossTestOpts{
		maxDatagramSize: 1200,
	})
	t.Logf("# establish initial RTT sample")
	test.send(initialSpace, 0, testSentPacketSize(1200))
	test.advance(10 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(initialSpace, 0)
	test.wantVar("min_rtt", 10*time.Millisecond)

	t.Logf("# send two packets spanning persistent congestion duration")
	test.send(initialSpace, 1, testSentPacketSize(1200))
	t.Logf("# 2000ms >> persistent congestion duration")
	test.advance(2000 * time.Millisecond)
	test.wantPTOExpired()
	test.send(initialSpace, 2, testSentPacketSize(1200))

	t.Logf("# trigger loss of previous packets")
	test.advance(10 * time.Millisecond)
	test.send(initialSpace, 3, testSentPacketSize(1200))
	test.advance(20 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{3, 4})
	test.wantAck(initialSpace, 3)
	test.wantLoss(initialSpace, 1, 2)
	t.Logf("# persistent congestion detected")

	test.send(initialSpace, 4, testSentPacketSize(1200))
	test.advance(20 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{4, 5})
	test.wantAck(initialSpace, 4)

	t.Logf("# min_rtt set from first sample after persistent congestion")
	test.wantVar("min_rtt", 20*time.Millisecond)
}

func TestLossInitialRTTSample(t *testing.T) {
	test := newLossTest(t, clientSide, lossTestOpts{})
	test.setMaxAckDelay(2 * time.Millisecond)
	t.Logf("# initial smoothed_rtt and rtt values")
	test.wantVar("smoothed_rtt", 333*time.Millisecond)
	test.wantVar("rttvar", 333*time.Millisecond/2)

	// https://www.rfc-editor.org/rfc/rfc9002.html#section-5.3-11
	t.Logf("# first RTT sample")
	test.send(initialSpace, 0)
	test.advance(10 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(initialSpace, 0)
	test.wantVar("latest_rtt", 10*time.Millisecond)
	t.Logf("# smoothed_rtt = latest_rtt")
	test.wantVar("smoothed_rtt", 10*time.Millisecond)
	t.Logf("# rttvar = latest_rtt / 2")
	test.wantVar("rttvar", 5*time.Millisecond)
}

func TestLossSmoothedRTTIgnoresMaxAckDelayBeforeHandshakeConfirmed(t *testing.T) {
	test := newLossTest(t, clientSide, lossTestOpts{})
	test.setMaxAckDelay(1 * time.Millisecond)
	test.send(initialSpace, 0)
	test.advance(10 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(initialSpace, 0)
	smoothedRTT := 10 * time.Millisecond
	rttvar := 5 * time.Millisecond

	// "[...] an endpoint [...] SHOULD ignore the peer's max_ack_delay
	// until the handshake is confirmed [...]"
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-5.3-7.2
	t.Logf("# subsequent RTT sample")
	test.send(handshakeSpace, 0)
	test.advance(20 * time.Millisecond)
	test.ack(handshakeSpace, 10*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(handshakeSpace, 0)
	test.wantVar("latest_rtt", 20*time.Millisecond)
	t.Logf("# ack_delay > max_ack_delay")
	t.Logf("# handshake not confirmed, so ignore max_ack_delay")
	t.Logf("# adjusted_rtt = latest_rtt - ackDelay")
	adjustedRTT := 10 * time.Millisecond
	t.Logf("# smoothed_rtt = 7/8 * smoothed_rtt + 1/8 * adjusted_rtt")
	smoothedRTT = (7*smoothedRTT + adjustedRTT) / 8
	test.wantVar("smoothed_rtt", smoothedRTT)
	rttvarSample := abs(smoothedRTT - adjustedRTT)
	t.Logf("# rttvar_sample = abs(smoothed_rtt - adjusted_rtt) = %v", rttvarSample)
	t.Logf("# rttvar = 3/4 * rttvar + 1/4 * rttvar_sample")
	rttvar = (3*rttvar + rttvarSample) / 4
	test.wantVar("rttvar", rttvar)
}

func TestLossSmoothedRTTUsesMaxAckDelayAfterHandshakeConfirmed(t *testing.T) {
	test := newLossTest(t, clientSide, lossTestOpts{})
	test.setMaxAckDelay(25 * time.Millisecond)
	test.send(initialSpace, 0)
	test.advance(10 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(initialSpace, 0)
	smoothedRTT := 10 * time.Millisecond
	rttvar := 5 * time.Millisecond

	test.confirmHandshake()

	// "[...] an endpoint [...] MUST use the lesser of the acknowledgment
	// delay and the peer's max_ack_delay after the handshake is confirmed [...]"
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-5.3-7.3
	t.Logf("# subsequent RTT sample")
	test.send(handshakeSpace, 0)
	test.advance(50 * time.Millisecond)
	test.ack(handshakeSpace, 40*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(handshakeSpace, 0)
	test.wantVar("latest_rtt", 50*time.Millisecond)
	t.Logf("# ack_delay > max_ack_delay")
	t.Logf("# handshake confirmed, so adjusted_rtt clamps to max_ack_delay")
	t.Logf("# adjusted_rtt = max_ack_delay")
	adjustedRTT := 25 * time.Millisecond
	rttvarSample := abs(smoothedRTT - adjustedRTT)
	t.Logf("# rttvar_sample = abs(smoothed_rtt - adjusted_rtt) = %v", rttvarSample)
	t.Logf("# rttvar = 3/4 * rttvar + 1/4 * rttvar_sample")
	rttvar = (3*rttvar + rttvarSample) / 4
	test.wantVar("rttvar", rttvar)
	t.Logf("# smoothed_rtt = 7/8 * smoothed_rtt + 1/8 * adjusted_rtt")
	smoothedRTT = (7*smoothedRTT + adjustedRTT) / 8
	test.wantVar("smoothed_rtt", smoothedRTT)
}

func TestLossAckDelayReducesRTTBelowMinRTT(t *testing.T) {
	test := newLossTest(t, clientSide, lossTestOpts{})
	test.send(initialSpace, 0)
	test.advance(10 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(initialSpace, 0)
	smoothedRTT := 10 * time.Millisecond
	rttvar := 5 * time.Millisecond

	// "[...] an endpoint [...] MUST NOT subtract the acknowledgment delay
	// from the RTT sample if the resulting value is smaller than the min_rtt."
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-5.3-7.4
	t.Logf("# subsequent RTT sample")
	test.send(handshakeSpace, 0)
	test.advance(12 * time.Millisecond)
	test.ack(handshakeSpace, 4*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(handshakeSpace, 0)
	test.wantVar("latest_rtt", 12*time.Millisecond)
	t.Logf("# latest_rtt - ack_delay < min_rtt, so adjusted_rtt = latest_rtt")
	adjustedRTT := 12 * time.Millisecond
	rttvarSample := abs(smoothedRTT - adjustedRTT)
	t.Logf("# rttvar_sample = abs(smoothed_rtt - adjusted_rtt) = %v", rttvarSample)
	t.Logf("# rttvar = 3/4 * rttvar + 1/4 * rttvar_sample")
	rttvar = (3*rttvar + rttvarSample) / 4
	test.wantVar("rttvar", rttvar)
	t.Logf("# smoothed_rtt = 7/8 * smoothed_rtt + 1/8 * adjusted_rtt")
	smoothedRTT = (7*smoothedRTT + adjustedRTT) / 8
	test.wantVar("smoothed_rtt", smoothedRTT)
}

func TestLossPacketThreshold(t *testing.T) {
	// "[...] the packet was sent kPacketThreshold packets before an
	// acknowledged packet [...]"
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-6.1.1
	test := newLossTest(t, clientSide, lossTestOpts{})
	t.Logf("# acking a packet triggers loss of packets sent kPacketThreshold earlier")
	test.send(appDataSpace, 0, 1, 2, 3, 4, 5, 6)
	test.ack(appDataSpace, 0*time.Millisecond, i64range[packetNumber]{4, 5})
	test.wantAck(appDataSpace, 4)
	test.wantLoss(appDataSpace, 0, 1)
}

func TestLossOutOfOrderAcks(t *testing.T) {
	test := newLossTest(t, clientSide, lossTestOpts{})
	t.Logf("# out of order acks, no loss")
	test.send(appDataSpace, 0, 1, 2)
	test.ack(appDataSpace, 0*time.Millisecond, i64range[packetNumber]{2, 3})
	test.wantAck(appDataSpace, 2)

	test.ack(appDataSpace, 0*time.Millisecond, i64range[packetNumber]{1, 2})
	test.wantAck(appDataSpace, 1)

	test.ack(appDataSpace, 0*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(appDataSpace, 0)
}

func TestLossSendAndAck(t *testing.T) {
	test := newLossTest(t, clientSide, lossTestOpts{})
	test.send(appDataSpace, 0, 1, 2)
	test.ack(appDataSpace, 0*time.Millisecond, i64range[packetNumber]{0, 3})
	test.wantAck(appDataSpace, 0, 1, 2)
	// Redundant ACK doesn't trigger more ACK events.
	// (If we did get an extra ACK, the test cleanup would notice and complain.)
	test.ack(appDataSpace, 0*time.Millisecond, i64range[packetNumber]{0, 3})
}

func TestLossAckEveryOtherPacket(t *testing.T) {
	test := newLossTest(t, clientSide, lossTestOpts{})
	test.send(appDataSpace, 0, 1, 2, 3, 4, 5, 6)
	test.ack(appDataSpace, 0*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(appDataSpace, 0)

	test.ack(appDataSpace, 0*time.Millisecond, i64range[packetNumber]{2, 3})
	test.wantAck(appDataSpace, 2)

	test.ack(appDataSpace, 0*time.Millisecond, i64range[packetNumber]{4, 5})
	test.wantAck(appDataSpace, 4)
	test.wantLoss(appDataSpace, 1)

	test.ack(appDataSpace, 0*time.Millisecond, i64range[packetNumber]{6, 7})
	test.wantAck(appDataSpace, 6)
	test.wantLoss(appDataSpace, 3)
}

func TestLossMultipleSpaces(t *testing.T) {
	// "Loss detection is separate per packet number space [...]"
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-6-3
	test := newLossTest(t, clientSide, lossTestOpts{})
	t.Logf("# send packets in different spaces")
	test.send(initialSpace, 0, 1, 2)
	test.send(handshakeSpace, 0, 1, 2)
	test.send(appDataSpace, 0, 1, 2)

	t.Logf("# ack one packet in each space")
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{1, 2})
	test.wantAck(initialSpace, 1)

	test.ack(handshakeSpace, 0*time.Millisecond, i64range[packetNumber]{1, 2})
	test.wantAck(handshakeSpace, 1)

	test.ack(appDataSpace, 0*time.Millisecond, i64range[packetNumber]{1, 2})
	test.wantAck(appDataSpace, 1)

	t.Logf("# send more packets")
	test.send(initialSpace, 3, 4, 5)
	test.send(handshakeSpace, 3, 4, 5)
	test.send(appDataSpace, 3, 4, 5)

	t.Logf("# ack the last packet, triggering loss")
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{5, 6})
	test.wantAck(initialSpace, 5)
	test.wantLoss(initialSpace, 0, 2)

	test.ack(handshakeSpace, 0*time.Millisecond, i64range[packetNumber]{5, 6})
	test.wantAck(handshakeSpace, 5)
	test.wantLoss(handshakeSpace, 0, 2)

	test.ack(appDataSpace, 0*time.Millisecond, i64range[packetNumber]{5, 6})
	test.wantAck(appDataSpace, 5)
	test.wantLoss(appDataSpace, 0, 2)
}

func TestLossTimeThresholdFirstPacketLost(t *testing.T) {
	// "[...] the packet [...] was sent long enough in the past."
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-6.1-3.2
	test := newLossTest(t, clientSide, lossTestOpts{})
	t.Logf("# packet 0 lost after time threshold passes")
	test.send(initialSpace, 0, 1)
	test.advance(10 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{1, 2})
	test.wantAck(initialSpace, 1)

	t.Logf("# latest_rtt == smoothed_rtt")
	test.wantVar("smoothed_rtt", 10*time.Millisecond)
	test.wantVar("latest_rtt", 10*time.Millisecond)
	t.Logf("# timeout = 9/8 * max(smoothed_rtt, latest_rtt) - time_since_packet_sent")
	test.wantTimeout(((10 * time.Millisecond * 9) / 8) - 10*time.Millisecond)

	test.advanceToLossTimer()
	test.wantLoss(initialSpace, 0)
}

func TestLossTimeThreshold(t *testing.T) {
	// "The time threshold is:
	// max(kTimeThreshold * max(smoothed_rtt, latest_rtt), kGranularity)"
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-6.1.2-2
	for _, tc := range []struct {
		name        string
		initialRTT  time.Duration
		latestRTT   time.Duration
		wantTimeout time.Duration
	}{{
		name:        "rtt increasing",
		initialRTT:  10 * time.Millisecond,
		latestRTT:   20 * time.Millisecond,
		wantTimeout: 20 * time.Millisecond * 9 / 8,
	}, {
		name:        "rtt decreasing",
		initialRTT:  10 * time.Millisecond,
		latestRTT:   5 * time.Millisecond,
		wantTimeout: ((7*10*time.Millisecond + 5*time.Millisecond) / 8) * 9 / 8,
	}, {
		name:        "rtt less than timer granularity",
		initialRTT:  500 * time.Microsecond,
		latestRTT:   500 * time.Microsecond,
		wantTimeout: 1 * time.Millisecond,
	}} {
		t.Run(tc.name, func(t *testing.T) {
			test := newLossTest(t, clientSide, lossTestOpts{})
			t.Logf("# first ack establishes smoothed_rtt")
			test.send(initialSpace, 0)
			test.advance(tc.initialRTT)
			test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 1})
			test.wantAck(initialSpace, 0)

			t.Logf("# ack of packet 2 starts loss timer for packet 1")
			test.send(initialSpace, 1, 2)
			test.advance(tc.latestRTT)
			test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{2, 3})
			test.wantAck(initialSpace, 2)

			t.Logf("# smoothed_rtt = %v", test.c.rtt.smoothedRTT)
			t.Logf("# latest_rtt = %v", test.c.rtt.latestRTT)
			t.Logf("# timeout = max(9/8 * max(smoothed_rtt, latest_rtt), 1ms)")
			t.Logf("#           (measured since packet 1 sent)")
			test.wantTimeout(tc.wantTimeout - tc.latestRTT)

			t.Logf("# advancing to the loss time causes loss of packet 1")
			test.advanceToLossTimer()
			test.wantLoss(initialSpace, 1)
		})
	}
}

func TestLossPTONotAckEliciting(t *testing.T) {
	// "When an ack-eliciting packet is transmitted,
	// the sender schedules a timer for the PTO period [...]"
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.1-1
	test := newLossTest(t, clientSide, lossTestOpts{})
	t.Logf("# PTO timer for first packet")
	test.send(initialSpace, 0)
	test.wantVar("smoothed_rtt", 333*time.Millisecond) // initial value
	test.wantVar("rttvar", 333*time.Millisecond/2)     // initial value
	t.Logf("# PTO = smoothed_rtt + max(4*rttvar, 1ms)")
	test.wantTimeout(999 * time.Millisecond)

	t.Logf("# sending a non-ack-eliciting packet doesn't adjust PTO")
	test.advance(333 * time.Millisecond)
	test.send(initialSpace, 1, sentPacket{
		ackEliciting: false,
	})
	test.wantVar("smoothed_rtt", 333*time.Millisecond) // unchanged
	test.wantVar("rttvar", 333*time.Millisecond/2)     // unchanged
	test.wantTimeout(666 * time.Millisecond)
}

func TestLossPTOMaxAckDelay(t *testing.T) {
	// "When the PTO is armed for Initial or Handshake packet number spaces,
	// the max_ack_delay in the PTO period computation is set to 0 [...]"
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.1-4
	test := newLossTest(t, clientSide, lossTestOpts{})
	t.Logf("# PTO timer for first packet")
	test.send(initialSpace, 0)
	test.wantVar("smoothed_rtt", 333*time.Millisecond) // initial value
	test.wantVar("rttvar", 333*time.Millisecond/2)     // initial value
	t.Logf("# PTO = smoothed_rtt + max(4*rttvar, 1ms)")
	test.wantTimeout(999 * time.Millisecond)

	test.advance(10 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(initialSpace, 0)

	t.Logf("# PTO timer for handshake packet")
	test.send(handshakeSpace, 0)
	test.wantVar("smoothed_rtt", 10*time.Millisecond)
	test.wantVar("rttvar", 5*time.Millisecond)
	t.Logf("# PTO = smoothed_rtt + max(4*rttvar, 1ms)")
	test.wantTimeout(30 * time.Millisecond)

	test.advance(10 * time.Millisecond)
	test.ack(handshakeSpace, 0*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(handshakeSpace, 0)
	test.confirmHandshake()

	t.Logf("# PTO timer for appdata packet")
	test.send(appDataSpace, 0)
	test.wantVar("smoothed_rtt", 10*time.Millisecond)
	test.wantVar("rttvar", 3750*time.Microsecond)
	t.Logf("# PTO = smoothed_rtt + max(4*rttvar, 1ms) + max_ack_delay (25ms)")
	test.wantTimeout(50 * time.Millisecond)
}

func TestLossPTOUnderTimerGranularity(t *testing.T) {
	// "The PTO period MUST be at least kGranularity [...]"
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.1-5
	test := newLossTest(t, clientSide, lossTestOpts{})
	test.send(initialSpace, 0)
	test.advance(10 * time.Microsecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(initialSpace, 0)

	test.send(initialSpace, 1)
	test.wantVar("smoothed_rtt", 10*time.Microsecond)
	test.wantVar("rttvar", 5*time.Microsecond)
	t.Logf("# PTO = smoothed_rtt + max(4*rttvar, 1ms)")
	test.wantTimeout(10*time.Microsecond + 1*time.Millisecond)
}

func TestLossPTOMultipleSpaces(t *testing.T) {
	// "[...] the timer MUST be set to the earlier value of the Initial and Handshake
	// packet number spaces."
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.1-6
	test := newLossTest(t, clientSide, lossTestOpts{})
	t.Logf("# PTO timer for first packet")
	test.send(initialSpace, 0)
	test.wantVar("smoothed_rtt", 333*time.Millisecond) // initial value
	test.wantVar("rttvar", 333*time.Millisecond/2)     // initial value
	t.Logf("# PTO = smoothed_rtt + max(4*rttvar, 1ms)")
	test.wantTimeout(999 * time.Millisecond)

	t.Logf("# Initial and Handshake packets in flight, first takes precedence")
	test.advance(333 * time.Millisecond)
	test.send(handshakeSpace, 0)
	test.wantTimeout(666 * time.Millisecond)

	t.Logf("# Initial packet acked, Handshake PTO timer armed")
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(initialSpace, 0)
	test.wantTimeout(999 * time.Millisecond)

	t.Logf("# send Initial, earlier Handshake PTO takes precedence")
	test.advance(333 * time.Millisecond)
	test.send(initialSpace, 1)
	test.wantTimeout(666 * time.Millisecond)
}

func TestLossPTOHandshakeConfirmation(t *testing.T) {
	// "An endpoint MUST NOT set its PTO timer for the Application Data
	// packet number space until the handshake is confirmed."
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.1-7
	test := newLossTest(t, clientSide, lossTestOpts{})
	test.send(initialSpace, 0)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(initialSpace, 0)

	test.send(handshakeSpace, 0)
	test.ack(handshakeSpace, 0*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(handshakeSpace, 0)

	test.send(appDataSpace, 0)
	test.wantNoTimeout()
}

func TestLossPTOBackoffDoubles(t *testing.T) {
	// "When a PTO timer expires, the PTO backoff MUST be increased,
	// resulting in the PTO period being set to twice its current value."
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.1-9
	test := newLossTest(t, serverSide, lossTestOpts{})
	test.datagramReceived(1200)
	test.send(initialSpace, 0)
	test.wantVar("smoothed_rtt", 333*time.Millisecond) // initial value
	test.wantVar("rttvar", 333*time.Millisecond/2)     // initial value
	t.Logf("# PTO = smoothed_rtt + max(4*rttvar, 1ms)")
	test.wantTimeout(999 * time.Millisecond)

	t.Logf("# wait for PTO timer expiration")
	test.advanceToLossTimer()
	test.wantPTOExpired()
	test.wantNoTimeout()

	t.Logf("# PTO timer doubles")
	test.send(initialSpace, 1)
	test.wantTimeout(2 * 999 * time.Millisecond)
	test.advanceToLossTimer()
	test.wantPTOExpired()
	test.wantNoTimeout()

	t.Logf("# PTO timer doubles again")
	test.send(initialSpace, 2)
	test.wantTimeout(4 * 999 * time.Millisecond)
	test.advanceToLossTimer()
	test.wantPTOExpired()
	test.wantNoTimeout()
}

func TestLossPTOBackoffResetOnAck(t *testing.T) {
	// "The PTO backoff factor is reset when an acknowledgment is received [...]"
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.1-9
	test := newLossTest(t, serverSide, lossTestOpts{})
	test.datagramReceived(1200)

	t.Logf("# first ack establishes smoothed_rtt = 10ms")
	test.send(initialSpace, 0)
	test.advance(10 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(initialSpace, 0)
	t.Logf("# set rttvar for simplicity")
	test.setRTTVar(0)

	t.Logf("# send packet 1 and wait for PTO")
	test.send(initialSpace, 1)
	test.wantTimeout(11 * time.Millisecond)
	test.advanceToLossTimer()
	test.wantPTOExpired()
	test.wantNoTimeout()

	t.Logf("# send packet 2 & 3, PTO doubles")
	test.send(initialSpace, 2, 3)
	test.wantTimeout(22 * time.Millisecond)

	test.advance(10 * time.Millisecond)
	t.Logf("# check remaining PTO (22ms - 10ms elapsed)")
	test.wantTimeout(12 * time.Millisecond)

	t.Logf("# ACK to packet 2 resets PTO")
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 3})
	test.wantAck(initialSpace, 1)
	test.wantAck(initialSpace, 2)

	t.Logf("# check remaining PTO (11ms - 10ms elapsed)")
	test.wantTimeout(1 * time.Millisecond)
}

func TestLossPTOBackoffNotResetOnClientInitialAck(t *testing.T) {
	// "[...] a client does not reset the PTO backoff factor on
	// receiving acknowledgments in Initial packets."
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.1-9
	test := newLossTest(t, clientSide, lossTestOpts{})

	t.Logf("# first ack establishes smoothed_rtt = 10ms")
	test.send(initialSpace, 0)
	test.advance(10 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(initialSpace, 0)
	t.Logf("# set rttvar for simplicity")
	test.setRTTVar(0)

	t.Logf("# send packet 1 and wait for PTO")
	test.send(initialSpace, 1)
	test.wantTimeout(11 * time.Millisecond)
	test.advanceToLossTimer()
	test.wantPTOExpired()
	test.wantNoTimeout()

	t.Logf("# send more packets, PTO doubles")
	test.send(initialSpace, 2, 3)
	test.send(handshakeSpace, 0)
	test.wantTimeout(22 * time.Millisecond)

	test.advance(10 * time.Millisecond)
	t.Logf("# check remaining PTO (22ms - 10ms elapsed)")
	test.wantTimeout(12 * time.Millisecond)

	// TODO: Is this right? 6.2.1-9 says we don't reset the PTO *backoff*, not the PTO.
	// 6.2.1-8 says we reset the PTO timer when an ack-eliciting packet is sent *or
	// acknowledged*, but the pseudocode in appendix A doesn't appear to do the latter.
	t.Logf("# ACK to Initial packet does not reset PTO for client")
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 3})
	test.wantAck(initialSpace, 1)
	test.wantAck(initialSpace, 2)
	t.Logf("# check remaining PTO (22ms - 10ms elapsed)")
	test.wantTimeout(12 * time.Millisecond)

	t.Logf("# ACK to handshake packet does reset PTO")
	test.ack(handshakeSpace, 0*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(handshakeSpace, 0)
	t.Logf("# check remaining PTO (12ms - 10ms elapsed)")
	test.wantTimeout(1 * time.Millisecond)
}

func TestLossPTONotSetWhenLossTimerSet(t *testing.T) {
	// "The PTO timer MUST NOT be set if a timer is set
	// for time threshold loss detection [...]"
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.1-12
	test := newLossTest(t, serverSide, lossTestOpts{})
	test.datagramReceived(1200)
	t.Logf("# PTO timer set for first packets sent")
	test.send(initialSpace, 0, 1)
	test.wantVar("smoothed_rtt", 333*time.Millisecond) // initial value
	test.wantVar("rttvar", 333*time.Millisecond/2)     // initial value
	t.Logf("# PTO = smoothed_rtt + max(4*rttvar, 1ms)")
	test.wantTimeout(999 * time.Millisecond)

	t.Logf("# ack of packet 1 starts loss timer for 0, PTO overidden")
	test.advance(333 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{1, 2})
	test.wantAck(initialSpace, 1)

	t.Logf("# latest_rtt == smoothed_rtt")
	test.wantVar("smoothed_rtt", 333*time.Millisecond)
	test.wantVar("latest_rtt", 333*time.Millisecond)
	t.Logf("# timeout = 9/8 * max(smoothed_rtt, latest_rtt) - time_since_packet_sent")
	test.wantTimeout(((333 * time.Millisecond * 9) / 8) - 333*time.Millisecond)
}

func TestLossDiscardingKeysResetsTimers(t *testing.T) {
	// "When Initial or Handshake keys are discarded,
	// the PTO and loss detection timers MUST be reset"
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.2-3
	test := newLossTest(t, clientSide, lossTestOpts{})

	t.Logf("# handshake packet sent 1ms after initial")
	test.send(initialSpace, 0, 1)
	test.advance(1 * time.Millisecond)
	test.send(handshakeSpace, 0, 1)
	test.advance(9 * time.Millisecond)

	t.Logf("# ack of Initial packet 2 starts loss timer for packet 1")
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{1, 2})
	test.wantAck(initialSpace, 1)

	test.advance(1 * time.Millisecond)
	t.Logf("# smoothed_rtt = %v", 10*time.Millisecond)
	t.Logf("# latest_rtt = %v", 10*time.Millisecond)
	t.Logf("# timeout = max(9/8 * max(smoothed_rtt, latest_rtt), 1ms)")
	t.Logf("#           (measured since Initial packet 1 sent)")
	test.wantTimeout((10 * time.Millisecond * 9 / 8) - 11*time.Millisecond)

	t.Logf("# ack of Handshake packet 2 starts loss timer for packet 1")
	test.ack(handshakeSpace, 0*time.Millisecond, i64range[packetNumber]{1, 2})
	test.wantAck(handshakeSpace, 1)

	t.Logf("# dropping Initial keys sets timer to Handshake timeout")
	test.discardKeys(initialSpace)
	test.wantTimeout((10 * time.Millisecond * 9 / 8) - 10*time.Millisecond)
}

func TestLossNoPTOAtAntiAmplificationLimit(t *testing.T) {
	// "If no additional data can be sent [because the server is at the
	// anti-amplification limit], the server's PTO timer MUST NOT be armed [...]"
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.2.1-1
	test := newLossTest(t, serverSide, lossTestOpts{
		maxDatagramSize: 1 << 20, // large initial congestion window
	})
	test.datagramReceived(1200)
	test.send(initialSpace, 0, sentPacket{
		ackEliciting: true,
		inFlight:     true,
		size:         1200,
	})
	test.wantTimeout(999 * time.Millisecond)

	t.Logf("PTO timer should be disabled when at the anti-amplification limit")
	test.send(initialSpace, 1, sentPacket{
		ackEliciting: false,
		inFlight:     true,
		size:         2 * 1200,
	})
	test.wantNoTimeout()

	// "When the server receives a datagram from the client, the amplification
	// limit is increased and the server resets the PTO timer."
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.2.1-2
	t.Logf("PTO timer should be reset when datagrams are received")
	test.datagramReceived(1200)
	test.wantTimeout(999 * time.Millisecond)

	// "If the PTO timer is then set to a time in the past, it is executed immediately."
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.2.1-2
	test.send(initialSpace, 2, sentPacket{
		ackEliciting: true,
		inFlight:     true,
		size:         3 * 1200,
	})
	test.wantNoTimeout()
	t.Logf("resetting expired PTO timer should exeute immediately")
	test.advance(1000 * time.Millisecond)
	test.datagramReceived(1200)
	test.wantPTOExpired()
	test.wantNoTimeout()
}

func TestLossClientSetsPTOWhenHandshakeUnacked(t *testing.T) {
	// "[...] the client MUST set the PTO timer if the client has not
	// received an acknowledgment for any of its Handshake packets and
	// the handshake is not confirmed [...]"
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.2.1-3
	test := newLossTest(t, clientSide, lossTestOpts{})
	test.send(initialSpace, 0)

	test.wantVar("smoothed_rtt", 333*time.Millisecond) // initial value
	test.wantVar("rttvar", 333*time.Millisecond/2)     // initial value
	t.Logf("# PTO = smoothed_rtt + max(4*rttvar, 1ms)")
	test.wantTimeout(999 * time.Millisecond)

	test.advance(333 * time.Millisecond)
	test.wantTimeout(666 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(initialSpace, 0)
	t.Logf("# PTO timer set for a client before handshake ack even if no packets in flight")
	test.wantTimeout(999 * time.Millisecond)

	test.advance(333 * time.Millisecond)
	test.wantTimeout(666 * time.Millisecond)
}

func TestLossKeysDiscarded(t *testing.T) {
	// "The sender MUST discard all recovery state associated with
	// [packets in number spaces with discarded keys] and MUST remove
	// them from the count of bytes in flight."
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-6.4-1
	test := newLossTest(t, clientSide, lossTestOpts{})
	test.send(initialSpace, 0, testSentPacketSize(1200))
	test.send(handshakeSpace, 0, testSentPacketSize(600))
	test.wantVar("bytes_in_flight", 1800)

	test.discardKeys(initialSpace)
	test.wantVar("bytes_in_flight", 600)

	test.discardKeys(handshakeSpace)
	test.wantVar("bytes_in_flight", 0)
}

func TestLossInitialCongestionWindow(t *testing.T) {
	// "Endpoints SHOULD use an initial congestion window of [...]"
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-7.2-1

	// "[...] 10 times the maximum datagram size [...]"
	test := newLossTest(t, clientSide, lossTestOpts{
		maxDatagramSize: 1200,
	})
	t.Logf("# congestion_window = 10*max_datagram_size (1200)")
	test.wantVar("congestion_window", 12000)

	// "[...] while limiting the window to the larger of 14720 bytes [...]"
	test = newLossTest(t, clientSide, lossTestOpts{
		maxDatagramSize: 1500,
	})
	t.Logf("# congestion_window limited to 14720 bytes")
	test.wantVar("congestion_window", 14720)

	// "[...] or twice the maximum datagram size."
	test = newLossTest(t, clientSide, lossTestOpts{
		maxDatagramSize: 10000,
	})
	t.Logf("# congestion_window limited to 2*max_datagram_size (10000)")
	test.wantVar("congestion_window", 20000)

	for _, tc := range []struct {
		maxDatagramSize  int
		wantInitialBurst int
	}{{
		// "[...] 10 times the maximum datagram size [...]"
		maxDatagramSize:  1200,
		wantInitialBurst: 12000,
	}, {
		// "[...] while limiting the window to the larger of 14720 bytes [...]"
		maxDatagramSize:  1500,
		wantInitialBurst: 14720,
	}, {
		// "[...] or twice the maximum datagram size."
		maxDatagramSize:  10000,
		wantInitialBurst: 20000,
	}} {
		t.Run(fmt.Sprintf("max_datagram_size=%v", tc.maxDatagramSize), func(t *testing.T) {
			test := newLossTest(t, clientSide, lossTestOpts{
				maxDatagramSize: tc.maxDatagramSize,
			})

			var num packetNumber
			window := tc.wantInitialBurst
			for window >= tc.maxDatagramSize {
				t.Logf("# %v bytes of initial congestion window remain", window)
				test.send(initialSpace, num, sentPacket{
					ackEliciting: true,
					inFlight:     true,
					size:         tc.maxDatagramSize,
				})
				window -= tc.maxDatagramSize
				num++
			}
			t.Logf("# congestion window (%v) < max_datagram_size, congestion control blocks send", window)
			test.wantSendLimit(ccLimited)
		})
	}
}

func TestLossBytesInFlight(t *testing.T) {
	test := newLossTest(t, clientSide, lossTestOpts{
		maxDatagramSize: 1200,
	})
	t.Logf("# sent packets are added to bytes_in_flight")
	test.wantVar("bytes_in_flight", 0)
	test.send(initialSpace, 0, testSentPacketSize(1200))
	test.wantVar("bytes_in_flight", 1200)
	test.send(initialSpace, 1, testSentPacketSize(800))
	test.wantVar("bytes_in_flight", 2000)

	t.Logf("# acked packets are removed from bytes_in_flight")
	test.advance(10 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{1, 2})
	test.wantAck(initialSpace, 1)
	test.wantVar("bytes_in_flight", 1200)

	t.Logf("# lost packets are removed from bytes_in_flight")
	test.advanceToLossTimer()
	test.wantLoss(initialSpace, 0)
	test.wantVar("bytes_in_flight", 0)
}

func TestLossCongestionWindowLimit(t *testing.T) {
	// "An endpoint MUST NOT send a packet if it would cause bytes_in_flight
	// [...] to be larger than the congestion window [...]"
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-7-7
	test := newLossTest(t, clientSide, lossTestOpts{
		maxDatagramSize: 1200,
	})
	t.Logf("# consume the initial congestion window")
	test.send(initialSpace, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, testSentPacketSize(1200))
	test.wantSendLimit(ccLimited)

	t.Logf("# give the pacer bucket time to refill")
	test.advance(333 * time.Millisecond) // initial RTT

	t.Logf("# sending limited by congestion window, not the pacer")
	test.wantVar("congestion_window", 12000)
	test.wantVar("bytes_in_flight", 12000)
	test.wantVar("pacer_bucket", 12000)
	test.wantSendLimit(ccLimited)

	t.Logf("# receiving an ack opens up the congestion window")
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(initialSpace, 0)
	test.wantSendLimit(ccOK)
}

func TestLossCongestionStates(t *testing.T) {
	test := newLossTest(t, clientSide, lossTestOpts{
		maxDatagramSize: 1200,
	})
	t.Logf("# consume the initial congestion window")
	test.send(initialSpace, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, testSentPacketSize(1200))
	test.wantSendLimit(ccLimited)
	test.wantVar("congestion_window", 12000)

	// "While a sender is in slow start, the congestion window
	// increases by the number of bytes acknowledged [...]"
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-7.3.1-2
	test.advance(333 * time.Millisecond)
	t.Logf("# congestion window increases by number of bytes acked (1200)")
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(initialSpace, 0)
	test.wantVar("congestion_window", 13200) // 12000 + 1200

	t.Logf("# congestion window increases by number of bytes acked (2400)")
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 3})
	test.wantAck(initialSpace, 1, 2)
	test.wantVar("congestion_window", 15600) // 12000 + 3*1200

	// TODO: ECN-CE count

	// "The sender MUST exit slow start and enter a recovery period
	// when a packet is lost [...]"
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-7.3.1-3
	t.Logf("# loss of a packet triggers entry to a recovery period")
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{6, 7})
	test.wantAck(initialSpace, 6)
	test.wantLoss(initialSpace, 3)

	// "On entering a recovery period, a sender MUST set the slow start
	// threshold to half the value of the congestion window when loss is detected."
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-7.3.2-2
	t.Logf("# slow_start_threshold = congestion_window / 2")
	test.wantVar("slow_start_threshold", 7800) // 15600/2

	// "[...] a single packet can be sent prior to reduction [of the congestion window]."
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-7.3.2-3
	test.send(initialSpace, 10, testSentPacketSize(1200))

	// "The congestion window MUST be set to the reduced value of the slow start
	// threshold before exiting the recovery period."
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-7.3.2-2
	t.Logf("# congestion window reduced to slow start threshold")
	test.wantVar("congestion_window", 7800)

	t.Logf("# acks for packets sent before recovery started do not affect congestion")
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 10})
	test.wantAck(initialSpace, 4, 5, 7, 8, 9)
	test.wantVar("slow_start_threshold", 7800)
	test.wantVar("congestion_window", 7800)

	// "A recovery period ends and the sender enters congestion avoidance when
	// a packet sent during the recovery period is acknowledged."
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-7.3.2-5
	t.Logf("# recovery ends and congestion avoidance begins when packet 10 is acked")
	test.advance(333 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 11})
	test.wantAck(initialSpace, 10)

	// "[...] limit the increase to the congestion window to at most one
	// maximum datagram size for each congestion window that is acknowledged."
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-7.3.3-2
	t.Logf("# after processing acks for one congestion window's worth of data...")
	test.send(initialSpace, 11, 12, 13, 14, 15, 16, testSentPacketSize(1200))
	test.advance(333 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 17})
	test.wantAck(initialSpace, 11, 12, 13, 14, 15, 16)
	t.Logf("# ...congestion window increases by max_datagram_size")
	test.wantVar("congestion_window", 9000) // 7800 + 1200

	// "The sender exits congestion avoidance and enters a recovery period
	// when a packet is lost [...]"
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-7.3.3-3
	test.send(initialSpace, 17, 18, 19, 20, 21, testSentPacketSize(1200))
	test.advance(333 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{18, 21})
	test.wantAck(initialSpace, 18, 19, 20)
	test.wantLoss(initialSpace, 17)
	t.Logf("# slow_start_threshold = congestion_window / 2")
	test.wantVar("slow_start_threshold", 4500)
}

func TestLossMinimumCongestionWindow(t *testing.T) {
	// "The RECOMMENDED [minimum congestion window] is 2 * max_datagram_size."
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-7.2-4
	test := newLossTest(t, clientSide, lossTestOpts{
		maxDatagramSize: 1200,
	})
	test.send(initialSpace, 0, 1, 2, 3, testSentPacketSize(1200))
	test.wantVar("congestion_window", 12000)

	t.Logf("# enter recovery")
	test.advance(333 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{3, 4})
	test.wantAck(initialSpace, 3)
	test.wantLoss(initialSpace, 0)
	test.wantVar("congestion_window", 6000)

	t.Logf("# enter congestion avoidance and return to recovery")
	test.send(initialSpace, 4, 5, 6, 7)
	test.advance(333 * time.Millisecond)
	test.wantLoss(initialSpace, 1, 2)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{7, 8})
	test.wantAck(initialSpace, 7)
	test.wantLoss(initialSpace, 4)
	test.wantVar("congestion_window", 3000)

	t.Logf("# enter congestion avoidance and return to recovery")
	test.send(initialSpace, 8, 9, 10, 11)
	test.advance(333 * time.Millisecond)
	test.wantLoss(initialSpace, 5, 6)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{11, 12})
	test.wantAck(initialSpace, 11)
	test.wantLoss(initialSpace, 8)
	t.Logf("# congestion window does not fall below 2*max_datagram_size")
	test.wantVar("congestion_window", 2400)

	t.Logf("# enter congestion avoidance and return to recovery")
	test.send(initialSpace, 12, 13, 14, 15)
	test.advance(333 * time.Millisecond)
	test.wantLoss(initialSpace, 9, 10)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{15, 16})
	test.wantAck(initialSpace, 15)
	test.wantLoss(initialSpace, 12)
	t.Logf("# congestion window does not fall below 2*max_datagram_size")
	test.wantVar("congestion_window", 2400)
}

func TestLossPersistentCongestion(t *testing.T) {
	// "When persistent congestion is declared, the sender's congestion
	// window MUST be reduced to the minimum congestion window [...]"
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-7.6.2-6
	test := newLossTest(t, clientSide, lossTestOpts{
		maxDatagramSize: 1200,
	})
	test.send(initialSpace, 0, testSentPacketSize(1200))
	test.c.cc.setUnderutilized(nil, true)

	test.advance(10 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(initialSpace, 0)

	t.Logf("# set rttvar for simplicity")
	test.setRTTVar(0)
	test.wantVar("smoothed_rtt", 10*time.Millisecond)
	t.Logf("# persistent congestion duration = 3*(smoothed_rtt + timerGranularity + max_ack_delay)")
	t.Logf("# persistent congestion duration = 108ms")

	t.Logf("# sending packets 1-5 over 108ms")
	test.send(initialSpace, 1, testSentPacketSize(1200))

	test.advance(11 * time.Millisecond) // total 11ms
	test.wantPTOExpired()
	test.send(initialSpace, 2, testSentPacketSize(1200))

	test.advance(22 * time.Millisecond) // total 33ms
	test.wantPTOExpired()
	test.send(initialSpace, 3, testSentPacketSize(1200))

	test.advance(44 * time.Millisecond) // total 77ms
	test.wantPTOExpired()
	test.send(initialSpace, 4, testSentPacketSize(1200))

	test.advance(31 * time.Millisecond) // total 108ms
	test.send(initialSpace, 5, testSentPacketSize(1200))
	t.Logf("# 108ms between packets 1-5")

	test.wantVar("congestion_window", 12000)
	t.Logf("# triggering loss of packets 1-5")
	test.send(initialSpace, 6, 7, 8, testSentPacketSize(1200))
	test.advance(10 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{8, 9})
	test.wantAck(initialSpace, 8)
	test.wantLoss(initialSpace, 1, 2, 3, 4, 5)

	t.Logf("# lost packets spanning persistent congestion duration")
	t.Logf("# congestion_window = 2 * max_datagram_size (minimum)")
	test.wantVar("congestion_window", 2400)
}

func TestLossSimplePersistentCongestion(t *testing.T) {
	// Simpler version of TestLossPersistentCongestion which acts as a
	// base for subsequent tests.
	test := newLossTest(t, clientSide, lossTestOpts{
		maxDatagramSize: 1200,
	})

	t.Logf("# establish initial RTT sample")
	test.send(initialSpace, 0, testSentPacketSize(1200))
	test.advance(10 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(initialSpace, 0)

	t.Logf("# send two packets spanning persistent congestion duration")
	test.send(initialSpace, 1, testSentPacketSize(1200))
	t.Logf("# 2000ms >> persistent congestion duration")
	test.advance(2000 * time.Millisecond)
	test.wantPTOExpired()
	test.send(initialSpace, 2, testSentPacketSize(1200))

	t.Logf("# trigger loss of previous packets")
	test.advance(10 * time.Millisecond)
	test.send(initialSpace, 3, testSentPacketSize(1200))
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{3, 4})
	test.wantAck(initialSpace, 3)
	test.wantLoss(initialSpace, 1, 2)

	t.Logf("# persistent congestion detected")
	test.wantVar("congestion_window", 2400)
}

func TestLossPersistentCongestionAckElicitingPackets(t *testing.T) {
	// "These two packets MUST be ack-eliciting [...]"
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-7.6.2-3
	test := newLossTest(t, clientSide, lossTestOpts{
		maxDatagramSize: 1200,
	})

	t.Logf("# establish initial RTT sample")
	test.send(initialSpace, 0, testSentPacketSize(1200))
	test.advance(10 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(initialSpace, 0)

	t.Logf("# send two packets spanning persistent congestion duration")
	test.send(initialSpace, 1, testSentPacketSize(1200))
	t.Logf("# 2000ms >> persistent congestion duration")
	test.advance(2000 * time.Millisecond)
	test.wantPTOExpired()
	test.send(initialSpace, 2, sentPacket{
		inFlight:     true,
		ackEliciting: false,
		size:         1200,
	})
	test.send(initialSpace, 3, testSentPacketSize(1200)) // PTO probe

	t.Logf("# trigger loss of previous packets")
	test.advance(10 * time.Millisecond)
	test.send(initialSpace, 4, testSentPacketSize(1200))
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{3, 5})
	test.wantAck(initialSpace, 3)
	test.wantAck(initialSpace, 4)
	test.wantLoss(initialSpace, 1, 2)

	t.Logf("# persistent congestion not detected: packet 2 is not ack-eliciting")
	test.wantVar("congestion_window", (12000+1200+1200-1200)/2)
}

func TestLossNoPersistentCongestionWithoutRTTSample(t *testing.T) {
	// "The persistent congestion period SHOULD NOT start until there
	// is at least one RTT sample."
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-7.6.2-4
	test := newLossTest(t, clientSide, lossTestOpts{
		maxDatagramSize: 1200,
	})

	t.Logf("# packets sent before initial RTT sample")
	test.send(initialSpace, 0, testSentPacketSize(1200))
	test.advance(2000 * time.Millisecond)
	test.wantPTOExpired()
	test.send(initialSpace, 1, testSentPacketSize(1200))

	test.advance(10 * time.Millisecond)
	test.send(initialSpace, 2, testSentPacketSize(1200))

	t.Logf("# first ack establishes RTT sample")
	test.advance(10 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{2, 3})
	test.wantAck(initialSpace, 2)
	test.wantLoss(initialSpace, 0, 1)

	t.Logf("# loss of packets before initial RTT sample does not cause persistent congestion")
	test.wantVar("congestion_window", 12000/2)
}

func TestLossPacerRefillRate(t *testing.T) {
	// "A sender SHOULD pace sending of all in-flight packets based on
	// input from the congestion controller."
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-7.7-1
	test := newLossTest(t, clientSide, lossTestOpts{
		maxDatagramSize: 1200,
	})
	t.Logf("# consume the initial congestion window")
	test.send(initialSpace, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, testSentPacketSize(1200))
	test.wantSendLimit(ccLimited)
	test.wantVar("pacer_bucket", 0)
	test.wantVar("congestion_window", 12000)

	t.Logf("# first RTT sample establishes smoothed_rtt")
	rtt := 100 * time.Millisecond
	test.advance(rtt)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 10})
	test.wantAck(initialSpace, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
	test.wantVar("congestion_window", 24000) // 12000 + 10*1200
	test.wantVar("smoothed_rtt", rtt)

	t.Logf("# advance 1 RTT to let the pacer bucket refill completely")
	test.advance(100 * time.Millisecond)
	t.Logf("# pacer_bucket = initial_congestion_window")
	test.wantVar("pacer_bucket", 12000)

	t.Logf("# consume capacity from the pacer bucket")
	test.send(initialSpace, 10, testSentPacketSize(1200))
	test.wantVar("pacer_bucket", 10800) // 12000 - 1200
	test.send(initialSpace, 11, testSentPacketSize(600))
	test.wantVar("pacer_bucket", 10200) // 10800 - 600
	test.send(initialSpace, 12, testSentPacketSize(600))
	test.wantVar("pacer_bucket", 9600) // 10200 - 600
	test.send(initialSpace, 13, 14, 15, 16, testSentPacketSize(1200))
	test.wantVar("pacer_bucket", 4800) // 9600 - 4*1200

	t.Logf("# advance 1/10 of an RTT, bucket refills")
	test.advance(rtt / 10)
	t.Logf("# pacer_bucket += 1.25 * (1/10) * congestion_window")
	t.Logf("#              += 3000")
	test.wantVar("pacer_bucket", 7800)
}

func TestLossPacerNextSendTime(t *testing.T) {
	test := newLossTest(t, clientSide, lossTestOpts{
		maxDatagramSize: 1200,
	})
	t.Logf("# consume the initial congestion window")
	test.send(initialSpace, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, testSentPacketSize(1200))
	test.wantSendLimit(ccLimited)
	test.wantVar("pacer_bucket", 0)
	test.wantVar("congestion_window", 12000)

	t.Logf("# first RTT sample establishes smoothed_rtt")
	rtt := 100 * time.Millisecond
	test.advance(rtt)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 10})
	test.wantAck(initialSpace, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
	test.wantVar("congestion_window", 24000) // 12000 + 10*1200
	test.wantVar("smoothed_rtt", rtt)

	t.Logf("# advance 1 RTT to let the pacer bucket refill completely")
	test.advance(100 * time.Millisecond)
	t.Logf("# pacer_bucket = initial_congestion_window")
	test.wantVar("pacer_bucket", 12000)

	t.Logf("# consume the refilled pacer bucket")
	test.send(initialSpace, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, testSentPacketSize(1200))
	test.wantSendLimit(ccPaced)

	t.Logf("# refill rate = 1.25 * congestion_window / rtt")
	test.wantSendDelay(rtt / 25) // rtt / (1.25 * 24000 / 1200)

	t.Logf("# no capacity available yet")
	test.advance(rtt / 50)
	test.wantVar("pacer_bucket", -600)
	test.wantSendLimit(ccPaced)

	t.Logf("# capacity available")
	test.advance(rtt / 50)
	test.wantVar("pacer_bucket", 0)
	test.wantSendLimit(ccOK)
}

func TestLossCongestionWindowUnderutilized(t *testing.T) {
	// "When bytes in flight is smaller than the congestion window
	// and sending is not pacing limited [...] the congestion window
	// SHOULD NOT be increased in either slow start or congestion avoidance."
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-7.8-1
	test := newLossTest(t, clientSide, lossTestOpts{
		maxDatagramSize: 1200,
	})
	test.send(initialSpace, 0, testSentPacketSize(1200))
	test.setUnderutilized(true)
	t.Logf("# underutilized: %v", test.c.cc.underutilized)
	test.wantVar("congestion_window", 12000)

	test.advance(10 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 1})
	test.wantAck(initialSpace, 0)
	t.Logf("# congestion window does not increase, because window is underutilized")
	test.wantVar("congestion_window", 12000)

	t.Logf("# refill pacer bucket")
	test.advance(10 * time.Millisecond)
	test.wantVar("pacer_bucket", 12000)

	test.send(initialSpace, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, testSentPacketSize(1200))
	test.setUnderutilized(false)
	test.advance(10 * time.Millisecond)
	test.ack(initialSpace, 0*time.Millisecond, i64range[packetNumber]{0, 11})
	test.wantAck(initialSpace, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
	t.Logf("# congestion window increases")
	test.wantVar("congestion_window", 24000)
}

type lossTest struct {
	t      *testing.T
	c      lossState
	now    time.Time
	fates  map[spaceNum]packetFate
	failed bool
}

type lossTestOpts struct {
	maxDatagramSize int
}

func newLossTest(t *testing.T, side connSide, opts lossTestOpts) *lossTest {
	c := &lossTest{
		t:     t,
		now:   time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC),
		fates: make(map[spaceNum]packetFate),
	}
	maxDatagramSize := 1200
	if opts.maxDatagramSize != 0 {
		maxDatagramSize = opts.maxDatagramSize
	}
	c.c.init(side, maxDatagramSize, c.now)
	t.Cleanup(func() {
		if !c.failed {
			c.checkUnexpectedEvents()
		}
	})
	return c
}

type spaceNum struct {
	space numberSpace
	num   packetNumber
}

func (c *lossTest) checkUnexpectedEvents() {
	c.t.Helper()
	for sn, fate := range c.fates {
		c.t.Errorf("ERROR: unexpected %v: %v %v", fate, sn.space, sn.num)
	}
	if c.c.ptoExpired {
		c.t.Errorf("ERROR: PTO timer unexpectedly expired")
	}
}

func (c *lossTest) setSmoothedRTT(d time.Duration) {
	c.t.Helper()
	c.checkUnexpectedEvents()
	c.t.Logf("set smoothed_rtt to %v", d)
	c.c.rtt.smoothedRTT = d
}

func (c *lossTest) setRTTVar(d time.Duration) {
	c.t.Helper()
	c.checkUnexpectedEvents()
	c.t.Logf("set rttvar to %v", d)
	c.c.rtt.rttvar = d
}

func (c *lossTest) setUnderutilized(v bool) {
	c.t.Logf("set congestion window underutilized: %v", v)
	c.c.cc.setUnderutilized(nil, v)
}

func (c *lossTest) advance(d time.Duration) {
	c.t.Helper()
	c.checkUnexpectedEvents()
	c.t.Logf("advance time %v", d)
	c.now = c.now.Add(d)
	c.c.advance(c.now, c.onAckOrLoss)
}

func (c *lossTest) advanceToLossTimer() {
	c.t.Helper()
	c.checkUnexpectedEvents()
	d := c.c.timer.Sub(c.now)
	c.t.Logf("advance time %v (up to loss timer)", d)
	if d < 0 {
		c.t.Fatalf("loss timer is in the past")
	}
	c.now = c.c.timer
	c.c.advance(c.now, c.onAckOrLoss)
}

type testSentPacketSize int

func (c *lossTest) send(spaceID numberSpace, opts ...any) {
	c.t.Helper()
	c.checkUnexpectedEvents()
	var nums []packetNumber
	prototype := sentPacket{
		ackEliciting: true,
		inFlight:     true,
	}
	for _, o := range opts {
		switch o := o.(type) {
		case sentPacket:
			prototype = o
		case testSentPacketSize:
			prototype.size = int(o)
		case int:
			nums = append(nums, packetNumber(o))
		case packetNumber:
			nums = append(nums, o)
		case i64range[packetNumber]:
			for num := o.start; num < o.end; num++ {
				nums = append(nums, num)
			}
		}
	}
	c.t.Logf("send %v %v", spaceID, nums)
	limit, _ := c.c.sendLimit(c.now)
	if prototype.inFlight && limit != ccOK {
		c.t.Fatalf("congestion control blocks sending packet")
	}
	if !prototype.inFlight && limit == ccBlocked {
		c.t.Fatalf("congestion control blocks sending packet")
	}
	for _, num := range nums {
		sent := &sentPacket{}
		*sent = prototype
		sent.num = num
		c.c.packetSent(c.now, nil, spaceID, sent)
	}
}

func (c *lossTest) datagramReceived(size int) {
	c.t.Helper()
	c.checkUnexpectedEvents()
	c.t.Logf("receive %v-byte datagram", size)
	c.c.datagramReceived(c.now, size)
}

func (c *lossTest) ack(spaceID numberSpace, ackDelay time.Duration, rs ...i64range[packetNumber]) {
	c.t.Helper()
	c.checkUnexpectedEvents()
	c.c.receiveAckStart()
	var acked rangeset[packetNumber]
	for _, r := range rs {
		c.t.Logf("ack %v delay=%v [%v,%v)", spaceID, ackDelay, r.start, r.end)
		acked.add(r.start, r.end)
	}
	for i, r := range rs {
		c.t.Logf("ack %v delay=%v [%v,%v)", spaceID, ackDelay, r.start, r.end)
		c.c.receiveAckRange(c.now, spaceID, i, r.start, r.end, c.onAckOrLoss)
	}
	c.c.receiveAckEnd(c.now, nil, spaceID, ackDelay, c.onAckOrLoss)
}

func (c *lossTest) onAckOrLoss(space numberSpace, sent *sentPacket, fate packetFate) {
	c.t.Logf("%v %v %v", fate, space, sent.num)
	if _, ok := c.fates[spaceNum{space, sent.num}]; ok {
		c.t.Errorf("ERROR: duplicate %v for %v %v", fate, space, sent.num)
	}
	c.fates[spaceNum{space, sent.num}] = fate
}

func (c *lossTest) confirmHandshake() {
	c.t.Helper()
	c.checkUnexpectedEvents()
	c.t.Logf("confirm handshake")
	c.c.confirmHandshake()
}

func (c *lossTest) validateClientAddress() {
	c.t.Helper()
	c.checkUnexpectedEvents()
	c.t.Logf("validate client address")
	c.c.validateClientAddress()
}

func (c *lossTest) discardKeys(spaceID numberSpace) {
	c.t.Helper()
	c.checkUnexpectedEvents()
	c.t.Logf("discard %s keys", spaceID)
	c.c.discardKeys(c.now, nil, spaceID)
}

func (c *lossTest) setMaxAckDelay(d time.Duration) {
	c.t.Helper()
	c.checkUnexpectedEvents()
	c.t.Logf("set max_ack_delay = %v", d)
	c.c.setMaxAckDelay(d)
}

func (c *lossTest) wantAck(spaceID numberSpace, nums ...packetNumber) {
	c.t.Helper()
	for _, num := range nums {
		if c.fates[spaceNum{spaceID, num}] != packetAcked {
			c.t.Fatalf("expected ack for %v %v\n", spaceID, num)
		}
		delete(c.fates, spaceNum{spaceID, num})
	}
}

func (c *lossTest) wantLoss(spaceID numberSpace, nums ...packetNumber) {
	c.t.Helper()
	for _, num := range nums {
		if c.fates[spaceNum{spaceID, num}] != packetLost {
			c.t.Fatalf("expected loss of %v %v\n", spaceID, num)
		}
		delete(c.fates, spaceNum{spaceID, num})
	}
}

func (c *lossTest) wantPTOExpired() {
	c.t.Helper()
	if !c.c.ptoExpired {
		c.t.Fatalf("expected PTO timer to expire")
	} else {
		c.t.Logf("PTO TIMER EXPIRED")
	}
	c.c.ptoExpired = false
}

func (l ccLimit) String() string {
	switch l {
	case ccOK:
		return "ccOK"
	case ccBlocked:
		return "ccBlocked"
	case ccLimited:
		return "ccLimited"
	case ccPaced:
		return "ccPaced"
	}
	return "BUG"
}

func (c *lossTest) wantSendLimit(want ccLimit) {
	c.t.Helper()
	if got, _ := c.c.sendLimit(c.now); got != want {
		c.t.Fatalf("congestion control send limit is %v, want %v", got, want)
	}
}

func (c *lossTest) wantSendDelay(want time.Duration) {
	c.t.Helper()
	limit, next := c.c.sendLimit(c.now)
	if limit != ccPaced {
		c.t.Fatalf("congestion control limit is %v, want %v", limit, ccPaced)
	}
	got := next.Sub(c.now)
	if got != want {
		c.t.Fatalf("delay until next send is %v, want %v", got, want)
	}
}

func (c *lossTest) wantVar(name string, want any) {
	c.t.Helper()
	var got any
	switch name {
	case "latest_rtt":
		got = c.c.rtt.latestRTT
	case "min_rtt":
		got = c.c.rtt.minRTT
	case "smoothed_rtt":
		got = c.c.rtt.smoothedRTT
	case "rttvar":
		got = c.c.rtt.rttvar
	case "congestion_window":
		got = c.c.cc.congestionWindow
	case "slow_start_threshold":
		got = c.c.cc.slowStartThreshold
	case "bytes_in_flight":
		got = c.c.cc.bytesInFlight
	case "pacer_bucket":
		got = c.c.pacer.bucket
	default:
		c.t.Fatalf("unknown var %q", name)
	}
	if got != want {
		c.t.Fatalf("%v = %v, want %v\n", name, got, want)
	} else {
		c.t.Logf("%v = %v", name, got)
	}
}

func (c *lossTest) wantTimeout(want time.Duration) {
	c.t.Helper()
	if c.c.timer.IsZero() {
		c.t.Fatalf("loss detection timer is not set, want %v", want)
	}
	got := c.c.timer.Sub(c.now)
	if got != want {
		c.t.Fatalf("loss detection timer expires in %v, want %v", got, want)
	}
	c.t.Logf("loss detection timer expires in %v", got)
}

func (c *lossTest) wantNoTimeout() {
	c.t.Helper()
	if !c.c.timer.IsZero() {
		d := c.c.timer.Sub(c.now)
		c.t.Fatalf("loss detection timer expires in %v, want not set", d)
	}
	c.t.Logf("loss detection timer is not set")
}

func (f packetFate) String() string {
	switch f {
	case packetAcked:
		return "ACK"
	case packetLost:
		return "LOSS"
	default:
		panic("unknown packetFate")
	}
}

Youez - 2016 - github.com/yon3zu
LinuXploit