From 831120a7d6f4bab77b75192f2b253806f27d695e Mon Sep 17 00:00:00 2001 From: Nathan Date: Sun, 20 Oct 2024 12:40:54 -0700 Subject: [PATCH 1/7] add failing unit test when white is compared with transparent --- test/fixtures/8b.png | Bin 0 -> 3647 bytes test/fixtures/8diff.png | Bin 0 -> 4212 bytes test/test.js | 1 + 3 files changed, 1 insertion(+) create mode 100644 test/fixtures/8b.png create mode 100644 test/fixtures/8diff.png diff --git a/test/fixtures/8b.png b/test/fixtures/8b.png new file mode 100644 index 0000000000000000000000000000000000000000..6f003e957c5b75418b883115d692df063e73466f GIT binary patch literal 3647 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 F_&=1<&D{V1 literal 0 HcmV?d00001 diff --git a/test/test.js b/test/test.js index c1ab655..afd2fca 100644 --- a/test/test.js +++ b/test/test.js @@ -23,6 +23,7 @@ diffTest('5a', '5b', '5diff', options, 0); diffTest('6a', '6b', '6diff', options, 51); diffTest('6a', '6a', '6empty', {threshold: 0}, 0); diffTest('7a', '7b', '7diff', {diffColorAlt: [0, 255, 0]}, 2448); +diffTest('5a', '8b', '8diff', options, 32640); test('throws error if image sizes do not match', () => { assert.throws(() => match(new Uint8Array(8), new Uint8Array(9), null, 2, 1), 'Image sizes do not match'); From 279c915755584547b374afe290362692bc4856e1 Mon Sep 17 00:00:00 2001 From: Nathan Date: Sun, 20 Oct 2024 13:06:36 -0700 Subject: [PATCH 2/7] fix failing unit test --- index.js | 26 +++++++++++++++----------- test/fixtures/{8b.png => 8a.png} | Bin test/fixtures/8diff.png | Bin 4212 -> 3879 bytes test/test.js | 2 +- 4 files changed, 16 insertions(+), 12 deletions(-) rename test/fixtures/{8b.png => 8a.png} (100%) diff --git a/index.js b/index.js index 50dc4e5..2569a53 100644 --- a/index.js +++ b/index.js @@ -184,20 +184,24 @@ function colorDelta(img1, img2, k, m, yOnly) { let b2 = img2[m + 2]; let a2 = img2[m + 3]; - if (a1 === a2 && r1 === r2 && g1 === g2 && b1 === b2) return 0; + if (a1 === a2 && r1 === r2 && g1 === g2 && b1 === b2 && a1 === a2) return 0; + + const rBackground = 64 + 128 * (k % 2); + const gBackground = 64 + 128 * (Math.floor(k / 2) % 2); + const bBackground = 64 + 128 * (Math.floor(k / 4) % 2); if (a1 < 255) { a1 /= 255; - r1 = blend(r1, a1); - g1 = blend(g1, a1); - b1 = blend(b1, a1); + r1 = blend(r1, rBackground, a1); + g1 = blend(g1, gBackground, a1); + b1 = blend(b1, bBackground, a1); } if (a2 < 255) { a2 /= 255; - r2 = blend(r2, a2); - g2 = blend(g2, a2); - b2 = blend(b2, a2); + r2 = blend(r2, rBackground, a2); + g2 = blend(g2, gBackground, a2); + b2 = blend(b2, bBackground, a2); } const y1 = rgb2y(r1, g1, b1); @@ -219,9 +223,9 @@ function rgb2y(r, g, b) { return r * 0.29889531 + g * 0.58662247 + b * 0.1144822 function rgb2i(r, g, b) { return r * 0.59597799 - g * 0.27417610 - b * 0.32180189; } function rgb2q(r, g, b) { return r * 0.21147017 - g * 0.52261711 + b * 0.31114694; } -// blend semi-transparent color with white -function blend(c, a) { - return 255 + (c - 255) * a; +// blend semi-transparent color with background +function blend(c, cBackground, a) { + return cBackground + (c - cBackground) * a; } function drawPixel(output, pos, r, g, b) { @@ -235,6 +239,6 @@ function drawGrayPixel(img, i, alpha, output) { const r = img[i + 0]; const g = img[i + 1]; const b = img[i + 2]; - const val = blend(rgb2y(r, g, b), alpha * img[i + 3] / 255); + const val = blend(rgb2y(r, g, b), 255, alpha * img[i + 3] / 255); drawPixel(output, i, val, val, val); } diff --git a/test/fixtures/8b.png b/test/fixtures/8a.png similarity index 100% rename from test/fixtures/8b.png rename to test/fixtures/8a.png diff --git a/test/fixtures/8diff.png b/test/fixtures/8diff.png index b6295ad2b904685e1a094e79ec19269a639723b9..9627f6fbcc1ffe3c484fed5d65fa661ccbff4e3d 100644 GIT binary patch literal 3879 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 F_&=1<&D{V1 diff --git a/test/test.js b/test/test.js index afd2fca..7f852a9 100644 --- a/test/test.js +++ b/test/test.js @@ -23,7 +23,7 @@ diffTest('5a', '5b', '5diff', options, 0); diffTest('6a', '6b', '6diff', options, 51); diffTest('6a', '6a', '6empty', {threshold: 0}, 0); diffTest('7a', '7b', '7diff', {diffColorAlt: [0, 255, 0]}, 2448); -diffTest('5a', '8b', '8diff', options, 32640); +diffTest('8a', '5b', '8diff', options, 32896); test('throws error if image sizes do not match', () => { assert.throws(() => match(new Uint8Array(8), new Uint8Array(9), null, 2, 1), 'Image sizes do not match'); From 076dee0659b72708e06e6df4273c9562ae9e5e5b Mon Sep 17 00:00:00 2001 From: Nathan Date: Sun, 20 Oct 2024 13:13:18 -0700 Subject: [PATCH 3/7] fix failing unit test --- index.js | 6 +++--- test/fixtures/5diff.png | Bin 4126 -> 4125 bytes test/test.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 2569a53..73d4fb9 100644 --- a/index.js +++ b/index.js @@ -186,9 +186,9 @@ function colorDelta(img1, img2, k, m, yOnly) { if (a1 === a2 && r1 === r2 && g1 === g2 && b1 === b2 && a1 === a2) return 0; - const rBackground = 64 + 128 * (k % 2); - const gBackground = 64 + 128 * (Math.floor(k / 2) % 2); - const bBackground = 64 + 128 * (Math.floor(k / 4) % 2); + const rBackground = 48 + 159 * (k % 2); + const gBackground = 48 + 159 * (Math.floor(k / 2) % 2); + const bBackground = 48 + 159 * (Math.floor(k / 4) % 2); if (a1 < 255) { a1 /= 255; diff --git a/test/fixtures/5diff.png b/test/fixtures/5diff.png index 04b664d74ce1a278650ba7c4f0d0ce85cc564917..b6c3d6352ddea1bd61e8cbae75d3749b2c9a80eb 100644 GIT binary patch delta 2120 zcmYjSZA?>V6b6*(gl=w3HbDXBS7z6-;AK;1L8%KH8zXLm2JR>_QYqNl7(N!O<)YE< zhY?&zS?D%ORoIX2@*$<2)yigKj4*A1?Fv?DoF=Qg5)#&nu~^+X_qHJa`rh|_&U2pg zoQL+bpVCgPi@x8tzAom=M9J_>Et%ffWx44zS9U6k+xN03&imY_mBmlh>~Rl4+-+z# zwkjXH=z2IO!{nIg}v6Stwf=rFi zm&pxXmhZRBx<+TVYNW5|)~S+AhD*+S<0o3(mF!9nxdSg$`8%8RJ+{N|kmkiE_$`*D z*R47k}_MENrF^}cCitGk>{*g57g!h_7Fiu97KNhYo%_b38v;sJi4U(eA- zm##EXfO2zN6O=t*&su{Ba)W~1GMhJibzB!)TZc5VtoO#nl4&j5lEY)FE+2SkY<)Dq z<@vwss8S_4P5k%DeNnnN{}5;)%HN8JS@x{00%F&ytBR_Fhe(;{FHj*V_^_#>$99pX zmo!q?#F*zSB4{i3E!T;$Vwc6rb9&Eiuvp)pl``p^yfOOkpv{xoJn< zFsqiP1rkv-VCcQ%+ayVMaEl0$^i>4^WNMBcoyj1lqd=l>K}ooD6)(9RTsnf+D(6r7 zxdiMY`0pxE$N2L>B zvaqaptGO4L$p^SA7Dr=iF^JQmA1rj92Iq}=)`p?iQ-EEYNt)wLhVGZJC=qxKkJght zjd(Epw(xre*>emz^M(UUj$*;znd2Rf#%|c2G0zt`AGsqF#?!nQ4~&+dZvo97HJbpx zcsB%9039gQh<4%xQLGvYb&=aLm*NwiRiKbZ>aW{9FX&hJ%%~PcuGA|1=Rv6l)^XkP z$h6E?fRR2nS?Gh`W;A22gG&`0Elk4d#Y6kRvB?;mXg(!N9{`z}tJy)+TJRdIU^UD> zWrRtFl0qq>ZJ6_JBS=H)L@XrXY{89F^? zeiux*LgwpCYDZOM8HA&2%o3b+;3H&Giq6+3f-}40U9q?^uzwr)qP^U%1yhSYsXLQ^jnwa^gQ8V_|eUA`bkm&JS@ z4^L9``>@vh%pe`JYp~7FlX+{-yP|!+Lu*ug&1MT^A|W3dLQ*vv=^G0cnyGPk9eM6P zu1rX*($#_|zd?xhYW5`NCdM23x8a}qIjZQvTlR+V4u|&-#R`RO4OWdra*KD0?DS3O zHa@>kLmo4KeTXUOP$ot~Bo~_tw$Wwi3@8RHFeM5OV0&7!EJ|PnVNwpcRE^{sY+SM` zsbnvNRX9*^4xtKmk+5Atn2Tivb%^1hv%~+8`3dQ5$nDvpL=%%FQT!;5g1?VH{A^cu I&Yq+H0|Ti-JOBUy delta 2121 zcmYLKe@s(X6b1?kL;STNQW>D=7A+l{)?{-+8P%Ye&TJVr@PbScX+xpWiNwzG5D+)F z=*%n&BnnMNx5=IrT3%zdG%+zmN=64#r3h>@Ft93#wiwF_JLkR@`d52;?{~iQo$s7` zCHZRd4;w?r+c#B&Vj=kHZUhi4s z(CTQoqh=mI6bO?_+;3(LI7BCkCp!to_lzu4c22KTOd3hjIW}XnC+z{ujq7F#xB00^p|mU1M8>X-4mID5wIJ)a&S{c<3}WUxVPXg~(1ydVanobs|8fVZzq6DH^9PY{z`0huiprX-8=M~`C~mC%C80)PIj+Qr`)$7DVuc%f5I1d`NIsi>~0JhBNz1O0ce1b`(lKt%%>hQjS0aI@l_q&}8 zP(5cj#bgRf7^^IZR7Zn$*!n>zP*XwiKsYiy1@Y=@K-1U5W_%EI!$lUL3kRJmv4bd9 zI^B)ec2l(kgRm+jNZYBnvq3@oI`P^)=len8vihr~pj6j+W+^n9>sB60AiDv*1lnME zzWQ58uRa0RgJuOg(PhEl5o+J0umw9xi#(r$OrBraxoC7b@fwU^EA)P9Rhbp2gR{6P zMESCfUY@BJ4r(FMc06=R>5oR$6_X6w4W|AuP)oe)kQlI} z<`F0TG5Ud1Oe$$6r5^=j!e)uUB4F6Du98mgI{d-OKkmiE+g!d4i?gs9s~A%HLxDhG z7)Yp5sac6NTntAhvB;#P-5U-~qcUM)aQ^zS1xu*?utozj1Fca&5g9IL90WOiBQa-6 zSvqeqfdX@W#%6?K0@dFJ+uRc&YGCQW#z#_pb-0Z<^@NYg$F^9_OdM{CVEaePX$bzp zqygiA_MScEW|el-?u-U90g~`dWga%?OdoQ`avtTX(duj?h}y|~7es}H)c8D)k zO`;g#OU??uZW0^v{mflgMPNRW=bLgGlihKKpw@xm`V-HZnDwNWgqT*Bgn6VcOY)+W zupf}AUKs-&)auODuod|}^YAmOHHumRPgS5G@N5SLAGrABks)%x$wm^SAF6r5MD|qA zHY_u(Q-{`EaK-f0$=L%_o7g!Xf?_Ie$s178^asvb+>e}vgyPCunl?`YH4pQSAmmYk z#quvg{Kk&LOUE@7{aaaXrd8Ujhuup9GtAB3T@;`>U~9p`))e0y>B{w8VJf*7Vqvr0 z!{Y{}KRo{^V3H-Gxs1g%5BVZcFTw&dc;3SDU@=B;lLBr&1au4&NG_YQ!JrneSzIWH yD`qpuPFpah=L^K3hkZP5Jkaa|j_g~=O*~WU Date: Sun, 20 Oct 2024 13:22:29 -0700 Subject: [PATCH 4/7] increase sensitivity of transparent differenc detection by making pattern repeat with non-integer period. --- index.js | 4 ++-- test/fixtures/5diff.png | Bin 4125 -> 4126 bytes test/test.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 73d4fb9..25c3ed1 100644 --- a/index.js +++ b/index.js @@ -187,8 +187,8 @@ function colorDelta(img1, img2, k, m, yOnly) { if (a1 === a2 && r1 === r2 && g1 === g2 && b1 === b2 && a1 === a2) return 0; const rBackground = 48 + 159 * (k % 2); - const gBackground = 48 + 159 * (Math.floor(k / 2) % 2); - const bBackground = 48 + 159 * (Math.floor(k / 4) % 2); + const gBackground = 48 + 159 * (Math.floor(k / 1.616) % 2); + const bBackground = 48 + 159 * (Math.floor(k / 2.612) % 2); if (a1 < 255) { a1 /= 255; diff --git a/test/fixtures/5diff.png b/test/fixtures/5diff.png index b6c3d6352ddea1bd61e8cbae75d3749b2c9a80eb..5b833e3f46d188e16e05125d267a61c1586a6465 100644 GIT binary patch delta 2139 zcmYjSdrVVz6s8rX2>8H_O`^kP;ucFa(YwWs@z&^2XX2E`-T~Vbp+ae#I77E`ePCVO z0y9}_Y_gW>=rZxrmB($w6=E`H21XhPl~QT9IkZq;bFGB3(w+0WE!zKj`a9qG&Ue0Z z`uz(1if>m6#w_WFg}aNS+Ocu2BxZYo`EZXxqb;k|v1{+v<%q<6Ps-`F^(JHdz;nw_7m(%`GxHXQcn7YmB zV~UsS)Fz|Mo8F)@cYIkwrzLJLkU-?@h6dZflXv7UQuv1i$>h0?mi8T8ZyCaszyhSkofp)Tz zDF$;6F8iW+Y_Ht5NSWa6#T=XC;C+p@u{{uPlvSj5t@BF>#%~T$Qwx-&{CluyEkhe# z)Txoil7_wJj$h2IB)<`=4BCg5;qN*096OzheaI?$5pmKI_?<_j#*n4U#iSl@U4R+o zN05YEo)tqS6))APSCI;t^a(qi2NfYWga}s4TZs{|Dn>9+OD}|qeHqjP1iS?8DE>6} z%uI-zfVocn>a}jqYVje?Jr9VILR7~iYhm9vY&chP%4B59Qbs1}=CJ@blYlf{=Zo1i zs-E4_l5(HG7}eSHG*l$3z09UH$-HL}TzbHx&}h#(m2*L&cTxO7dk%Tj%`$liE}D(# zSwk-Bwgo%MI*;J`YTs3(m5EHTktFKaQg}u<)RcKQPuI0{{O5FJ0?ZJ~rd&uOZij-5 zK?8;%tfo5Ioaf-%5WcS3myo9rpp+(~!HGRGE~OBvC-Xk3+mG~_u*b24jD&RXNQS|% zrSm<=!@>5np<3b2Kq9K?p3mDHh(#iNe(M&b`3e*_vD?Wa-)9k=Y;5?XAd;}Z0wm4yrsG7rA)MWe-%ZZ%II1EVl z^WR&BmFd_CZ#c?-1`E#T*2LQ4YRWKAkMrs7Vg(=^S)SI!tRbMODx(drMBTt_b^*5e zK;=9fA*!X{i)rWCJYpY0%1n@S?AQ5GLGPhy%=;<S3ra^Z+Qz2)Q|vHOVEYruq})Y^PvS`+UV|0f3A6Wq z#krxRiC5Y0ybtPKa0Ug^yYLV;*Pn`yws13XXhzCkgtLJKw*a-qPkqM#{T%pG=Iumk zi>pGhOjHPs@fP;fU+r5?I1t55#(r!)yB&NfQM-lM1S~ryw~qvqp2`12iG`QSRg0hP z!gtU`u!@_3s~KGe657UP!!nwTn~wt%#(SEe4J(CU8gUb@3`L;}vrn*ZfJGuZ1CznM zO_Tc)Ajoe+EY>{3=>^XUbYRUlX+sPaus*(3$^=qSgJLHRzMd`5$7{sju#{@6Lc_RS zjs)E1==d?{9(24+WFp!tS6W@x!bfGklQ79yj()4_0o^?Lb6`I2QPK^}z(W8=9Gjw` zaFE77pDr2~%WcUzwYC=TDJ;w#jImXWYA2s>MKxgd8?l(;lgb!;6e;zRaqgrc@T?PqmGYzlW>yb) zdV@r(Y_Qh6q-80=bJ*tDk%%)Ru9&R3qH6Ic)8S(X+8aIcUI~)YSNg7E^Ze3j4Od3H zcPks%Fw6}v7Wq?x!*U2Ab}_Vq{ENXviGee*gB_N@9DaxSX-Gy6@W delta 2138 zcmYjSZA?>V6b6*(gl=w3HbDXBS7z6-VA<4JKIu%rTfucKBTmxTDzGTBd9H~UBN2EX|lSjAz{53tJR%zZwvCT?|t9rJkN8U zbLhzUIpgG-*n53z8{)o777fqT6Pe9c%MF*grb}Mlv4=T6IORPhFMp7A>?W6+_`BMDnOBe-Cf%pN`uRPmazEswJ_m zEn-2Dbj-J7#bufOkc>!AERvS#t(G4)&ALZtHmfDCY1b%HO!^D1yW_`=-Wq1PkI2Id z%0O3(uE&1pJ;J=u1iyvS%!U<>BQpn8k|Z9~e64TVVD#>0lD3Wc4DcYkr8=`BXOd3n zEI48%;a~%Y@GJFZz9zPEIilRy(gJ1oJ91Vbtunog*)&@?e0f|OU*CW z6N(hxWvj(`cG_7u%%~(8!DKWI2>LHfZ4hNT*#%;OeT!YRRTxG8sW1!H32DY(kVncVbvEr7;+D{n{EZ zl*`BTZakUthXmLLSC&{1=2&?g?4lRo9SHAL>sAFsooJ;vxDhoz)eyyCE;JoX8uED` zAbs+=U;8%K5>yb7aGG)*#`W+HbO*}Nt;ZfmM=ZKYfQ#U~E*|mzq}KzSJPjpHO@Pfd zzjhs;4In8)z^>0G%!wv_ z_bXUb9DDbk1l-aWv;}eclu!KYEZ`e6!=-2qlpb|u{ z)XM{d;M9HFxOQo@Tjoo^NEe^V>)@st!&u;C)A>YWCrD+$=EqaVz|qM#Y-y1+a{z2= ztz|suHJ>#Y!CL5j+6bKrCB=~`{88sA(Ta)Oyy2~40Zy*08ZTX;J?OegKe+^Z0|SoK zcB&-riv^3=HWQb;WqB|Z(#-v}z z742oW&xcwlZ9@^K)w*p_e_MB9%~5*e3k5Lx>2fA14+|te3o$*Dap-{%J#Ot~Kf`Ur zyEl39;Vd;uHu|-((fnik@v!k`=X>vUmyROdKr5g5Y}qi-}YMZoBit zm5%R={3+bv!d}5%heoi@87cuVBgKoP(|6#pa){2r8`=ciiWc(R?F%KQUN&Z45X2e} zwG_2GK+gGLwU`I-^aS-Gj5XIY`_=s1I+XcEDxb_~DmwN#H3r!?OfF9*y5z${NUBaP zd22pGGj&eCv(VeeR`DCFawVV1ZxN!SmSj%g;>7)i0dD#S<-g--ip+3i{?M#&B-W7B z=t^$$KP0-QCbaA4=Lt8PnY%tj7kA2&qf4X!wT0TLDoh5{0v5O>itF~HJ`GV7!_(rn zNvIadHQCuzMM}jUSXPmRf_(^AGYk0X;+MHlTHJsTPAWI@51H$b&W_xkFGx1gDI(cV R!Wj7b^yAOBcjxaq@;{QHN>~5@ diff --git a/test/test.js b/test/test.js index d89c45d..2640963 100644 --- a/test/test.js +++ b/test/test.js @@ -19,7 +19,7 @@ diffTest('2a', '2b', '2diff', { }, 12437); diffTest('3a', '3b', '3diff', options, 212); diffTest('4a', '4b', '4diff', options, 36049); -diffTest('5a', '5b', '5diff', options, 8); +diffTest('5a', '5b', '5diff', options, 1); diffTest('6a', '6b', '6diff', options, 51); diffTest('6a', '6a', '6empty', {threshold: 0}, 0); diffTest('7a', '7b', '7diff', {diffColorAlt: [0, 255, 0]}, 2448); From 751c1cedc583471fbec233db8c04d77103de2202 Mon Sep 17 00:00:00 2001 From: Nathan Date: Mon, 21 Oct 2024 07:03:19 -0700 Subject: [PATCH 5/7] remove repeated comparison --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 25c3ed1..092563c 100644 --- a/index.js +++ b/index.js @@ -184,7 +184,7 @@ function colorDelta(img1, img2, k, m, yOnly) { let b2 = img2[m + 2]; let a2 = img2[m + 3]; - if (a1 === a2 && r1 === r2 && g1 === g2 && b1 === b2 && a1 === a2) return 0; + if (a1 === a2 && r1 === r2 && g1 === g2 && b1 === b2) return 0; const rBackground = 48 + 159 * (k % 2); const gBackground = 48 + 159 * (Math.floor(k / 1.616) % 2); From a358d9eb7291f4ce92029e6794578e3b0856b28e Mon Sep 17 00:00:00 2001 From: Nathan Date: Mon, 21 Oct 2024 07:55:55 -0700 Subject: [PATCH 6/7] Add digits to color periods --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 092563c..731d914 100644 --- a/index.js +++ b/index.js @@ -187,8 +187,8 @@ function colorDelta(img1, img2, k, m, yOnly) { if (a1 === a2 && r1 === r2 && g1 === g2 && b1 === b2) return 0; const rBackground = 48 + 159 * (k % 2); - const gBackground = 48 + 159 * (Math.floor(k / 1.616) % 2); - const bBackground = 48 + 159 * (Math.floor(k / 2.612) % 2); + const gBackground = 48 + 159 * (Math.floor(k / 1.618033988749895) % 2); + const bBackground = 48 + 159 * (Math.floor(k / 2.618033988749895) % 2); if (a1 < 255) { a1 /= 255; From a45ec2a709a2a0bb31c0554f3eddcacb1e19d64e Mon Sep 17 00:00:00 2001 From: Nathan Date: Mon, 21 Oct 2024 12:20:35 -0700 Subject: [PATCH 7/7] fix unit test after changing coefficients ever so slightly --- test/fixtures/5diff.png | Bin 4126 -> 4125 bytes test/test.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fixtures/5diff.png b/test/fixtures/5diff.png index 5b833e3f46d188e16e05125d267a61c1586a6465..d7f3461363d2d842fc5c309e5b6711d36d34f58a 100644 GIT binary patch literal 4125 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 literal 4126 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