xpgen.php 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359
  1. <?php
  2. $MOD = gmp_init("0x16A6B036D7F2A79");
  3. // plus 1 for comparassion
  4. $max_uint64 = gmp_init("0xFFFFFFFFFFFFFFFF");
  5. $max_uint64 = gmp_add($max_uint64,1);
  6. $max_uint32 = gmp_init("0xFFFFFFFF");
  7. $max_uint32 = gmp_add($max_uint32,1);
  8. $max_uint16 = gmp_init("0xFFFF");
  9. $max_uint16 = gmp_add($max_uint16,1);
  10. $max_uint8 = gmp_init("0xFF");
  11. $max_uint8 = gmp_add($max_uint8,1);
  12. function gmp_shiftl($x,$n) { // shift left
  13. if(gettype($n)=="object") { // its a gmp number
  14. $n = gmp_intval($n);
  15. }
  16. return(gmp_mul($x,gmp_pow(2,$n)));
  17. }
  18. function gmp_shiftr($x,$n) { // shift right
  19. if(gettype($n)=="object") {
  20. $n = gmp_intval($n);
  21. }
  22. return(gmp_div($x,gmp_pow(2,$n)));
  23. }
  24. function uint64($in) { // makes number overflow like uint64
  25. global $max_uint64;
  26. $out = gmp_mod($in, $max_uint64);
  27. // underflow
  28. if (gmp_cmp($out, gmp_init(0)) < 0) {
  29. $out = gmp_add($out, $max_uint64);
  30. }
  31. return $out;
  32. }
  33. function uint32($in) { // makes number overflow like uint64
  34. global $max_uint32;
  35. $out = gmp_mod($in, $max_uint32);
  36. // underflow
  37. if (gmp_cmp($out, gmp_init(0)) < 0) {
  38. $out = gmp_add($out, $max_uint32);
  39. }
  40. return $out;
  41. }
  42. $max_int64 = gmp_init("9223372036854775807");
  43. $max_int64 = gmp_add($max_int64,1);
  44. $min_int64 = gmp_init("-9223372036854775808");
  45. function int64($in) {
  46. global $max_int64, $min_int64;
  47. // Handle overflow
  48. if (gmp_cmp($in, $max_int64) >= 0) {
  49. return gmp_sub($in, gmp_mul($max_int64, gmp_init(2)));
  50. }
  51. // Handle underflow
  52. elseif (gmp_cmp($in, $min_int64) < 0) {
  53. return gmp_add($in, gmp_mul($max_int64, gmp_init(2)));
  54. }
  55. return $in;
  56. }
  57. function uint8($in) {
  58. global $max_uint8;
  59. $out = gmp_mod($in, $max_uint8);
  60. // underflow
  61. if (gmp_cmp($out, gmp_init(0)) < 0) {
  62. $out = gmp_add($out, $max_uint8);
  63. }
  64. return $out;
  65. }
  66. function uint16($in) {
  67. global $max_uint16;
  68. $out = gmp_mod($in, $max_uint16);
  69. // underflow
  70. if (gmp_cmp($out, gmp_init(0)) < 0) {
  71. $out = gmp_add($out, $max_uint16);
  72. }
  73. return $out;
  74. }
  75. function residue_add($x, $y) {
  76. global $MOD;
  77. $z = uint64(gmp_add($x, $y));
  78. if(gmp_cmp($z, $MOD)>=0) {
  79. $z = uint64(gmp_sub($z, $MOD));
  80. }
  81. return $z;
  82. }
  83. function residue_sub($x, $y) {
  84. global $MOD;
  85. $z = uint64(gmp_sub($x, $y));
  86. if(gmp_cmp($x, $y)<0) {
  87. $z = uint64(gmp_add($z, $MOD));
  88. }
  89. return $z;
  90. }
  91. function __emulu($x, $y) {
  92. return uint64(gmp_mul($x,$y));
  93. }
  94. function __umul128($multiplier, $multiplicand, &$product_hi) {
  95. // multiplier = ab = a * 2^32 + b
  96. // multiplicand = cd = c * 2^32 + d
  97. // ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d
  98. $a = uint64(gmp_shiftr($multiplier, 32));
  99. $b = uint32($multiplier);
  100. $c = uint64(gmp_shiftr($multiplicand,32));
  101. $d = uint32($multiplicand);
  102. $ad = __emulu($a, $d);
  103. $bd = __emulu($b, $d);
  104. $adbc = uint64(gmp_add($ad,__emulu($b , $c)));
  105. if(gmp_cmp($adbc, $ad) < 0) {
  106. $adbc_carry = 1;
  107. } else {
  108. $adbc_carry = 0;
  109. }
  110. // multiplier * multiplicand = product_hi * 2^64 + product_lo
  111. $product_lo = uint64(gmp_add($bd,uint64(gmp_shiftl($adbc, 32))));
  112. if(gmp_cmp($product_lo, $bd) < 0) {
  113. $product_lo_carry = 1;
  114. } else {
  115. $product_lo_carry = 0;
  116. }
  117. $product_hi = uint64(gmp_add(uint64(gmp_add(uint64(gmp_add(__emulu($a , $c),uint64(gmp_shiftr($adbc, 32)))), uint64(gmp_shiftl($adbc_carry, 32)))), $product_lo_carry));
  118. return $product_lo;
  119. }
  120. function ui128_quotient_mod($lo, $hi) {
  121. // hi:lo * ceil(2**170/MOD) >> (64 + 64 + 42)
  122. $prod1 = gmp_init(0);
  123. __umul128($lo, gmp_init("0x604fa6a1c6346a87"), $prod1);
  124. $part1hi = gmp_init(0);
  125. $part1lo = __umul128($lo, gmp_init("0x2d351c6d04f8b"), $part1hi);
  126. $part2hi = gmp_init(0);
  127. $part2lo = __umul128($hi, gmp_init("0x604fa6a1c6346a87"), $part2hi);
  128. $sum1 = uint64(gmp_add($part1lo, $part2lo));
  129. $sum1carry = 0;
  130. if(gmp_cmp($sum1, $part1lo) < 0) {
  131. $sum1carry = 1;
  132. }
  133. $sum1 = uint64(gmp_add($sum1,$prod1));
  134. if(gmp_cmp($sum1, $prod1) < 0) {
  135. $sum1carry = $sum1carry + 1;
  136. }
  137. $sum1carry = gmp_init($sum1carry);
  138. $prod2 = uint64(gmp_add($part1hi, uint64(gmp_add($part2hi, $sum1carry))));
  139. $prod3hi = 0;
  140. $prod3lo = __umul128($hi, gmp_init("0x2d351c6d04f8b"), $prod3hi);
  141. $prod3lo = uint64(gmp_add($prod3lo, $prod2));
  142. if(gmp_cmp($prod3lo, $prod2) < 0) {
  143. $prod3hi = uint64(gmp_add($prod3hi, 1));
  144. }
  145. return uint64(gmp_or(uint64(gmp_shiftr($prod3lo, 42)),uint64(gmp_shiftl($prod3hi, 22))));
  146. }
  147. function residue_mul($x, $y) { // there is a bug
  148. global $MOD;
  149. // * ceil(2**170/MOD) = 0x2d351 c6d04f8b|604fa6a1 c6346a87 for (p-1)*(p-1) max
  150. $hi = gmp_init(0);
  151. $lo = __umul128($x, $y, $hi);
  152. $quotient = ui128_quotient_mod($lo,$hi);
  153. return uint64(gmp_sub($lo, uint64(gmp_mul($quotient, $MOD))));
  154. }
  155. function residue_pow($x, $y) {
  156. if(gmp_cmp($y,0)==0) {
  157. return gmp_init(1);
  158. }
  159. $cur = $x;
  160. while(! (gmp_cmp(gmp_and($y,1),0)>0)) { //while (!(y & 1)) {
  161. $cur = residue_mul($cur, $cur);
  162. $y = uint64(gmp_shiftr($y, 1));
  163. }
  164. $res = $cur;
  165. while (gmp_cmp($y,0)!=0) { // while ((y >>= 1) != 0) {
  166. $y = uint64(gmp_shiftr($y, 1));
  167. $cur = residue_mul($cur, $cur);
  168. if(gmp_cmp(gmp_and($y, 1),0)>0) {
  169. $res = residue_mul($res, $cur);
  170. }
  171. }
  172. $y = uint64(gmp_shiftr($y, 1));
  173. return $res;
  174. }
  175. function inverse($u, $v) {
  176. $tmp = 0; // i64
  177. $xu = 1; // i64
  178. $xv = 0; // i64
  179. $v0 = $v;
  180. while (gmp_cmp($u,1)>0) {
  181. //ui64 d = v / u; ui64 remainder = v % u;
  182. $d = uint64(gmp_div($v,$u));
  183. $remainder = uint64(gmp_mod($v, $u));
  184. $tmp = int64($u);
  185. $u = $remainder;
  186. $v = uint64($tmp);
  187. $tmp = $xu;
  188. $xu = int64(gmp_sub($xv,gmp_mul($d, $xu)));
  189. $xv = int64($tmp);
  190. }
  191. //xu += (xu < 0 ? v0 : 0);
  192. if(gmp_cmp($xu,0)<0) {
  193. $xu = int64(gmp_add($xu,$v0));
  194. }
  195. return uint64($xu);
  196. }
  197. function residue_inv($x) {
  198. global $MOD;
  199. return inverse($x, $MOD);
  200. }
  201. // #define BAD 0xFFFFFFFFFFFFFFFFull
  202. // #define NON_RESIDUE 43
  203. $BAD = gmp_init("0xFFFFFFFFFFFFFFFF");
  204. $NON_RESIDUE = gmp_init("43");
  205. function residue_sqrt($what) {
  206. global $BAD, $MOD, $NON_RESIDUE, $debugEN;
  207. // if(!what) - if zero
  208. if (gmp_cmp($what,0)==0)
  209. return gmp_init(0);
  210. $g = $NON_RESIDUE;
  211. $z = gmp_init(0);
  212. $y = $z;
  213. $r = $z;
  214. $x = $z;
  215. $b = $z;
  216. $t = $z;
  217. $e = $z;
  218. $q = uint64(gmp_sub($MOD,1));
  219. //while (!(q & 1))
  220. // e++, q >>= 1;
  221. while (! (gmp_cmp(gmp_and($q, 1),0)>0)) {
  222. $e = uint64(gmp_add($e, 1));
  223. $q = uint64(gmp_shiftr($q, 1));
  224. }
  225. $z = residue_pow($g, $q);
  226. $y = $z;
  227. $r = $e;
  228. $x = residue_pow($what, uint64(gmp_div(uint64(gmp_sub($q,1)),2))); // (q - 1) / 2
  229. $b = residue_mul(residue_mul($what, $x), $x);
  230. $x = residue_mul($what, $x);
  231. while (gmp_cmp($b,1)!=0) {
  232. $m = gmp_init(0);
  233. $b2 = $b;
  234. do {
  235. $m = uint64(gmp_add($m,1));
  236. $b2 = residue_mul($b2, $b2);
  237. } while (gmp_cmp($b2,1)!=0);
  238. if (gmp_cmp($m, $r)==0)
  239. return $BAD;
  240. $t = residue_pow($y, uint64(gmp_shiftl(1, uint64(gmp_sub(uint64(gmp_sub($r, $m)), 1)))));
  241. $y = residue_mul($t, $t);
  242. $r = $m;
  243. $x = residue_mul($x, $t);
  244. $b = residue_mul($b, $y);
  245. }
  246. if (gmp_cmp(residue_mul($x, $x),$what)!=0) {
  247. //printf("internal error in sqrt\n");
  248. return $BAD;
  249. }
  250. return $x;
  251. }
  252. /*
  253. typedef struct {
  254. ui64 u[2];
  255. ui64 v[2];
  256. } TDivisor;
  257. */
  258. class TDivisor {
  259. public $u;
  260. public $v;
  261. }
  262. $f = Array(gmp_init(0), gmp_init("0x21840136C85381"),gmp_init("0x44197B83892AD0"),gmp_init("0x1400606322B3B04"),gmp_init("0x1400606322B3B04"),gmp_init("1"));
  263. function find_divisor_v(&$d) { // int find_divisor_v(TDivisor* d), u[] input v[] output
  264. global $f, $BAD;
  265. // u | v^2 - f
  266. // u = u0 + u1*x + x^2
  267. // f%u = f0 + f1*x
  268. $v1 = gmp_init(0);
  269. $f2 = Array(); //ui64 f2[6];
  270. //int i, j;
  271. $i = 0;
  272. $j = 0;
  273. for ($i = 0; $i < 6; $i++) {
  274. $f2[$i] = $f[$i];
  275. }
  276. $u0 = $d->u[0];
  277. $u1 = $d->u[1];
  278. for($j=3; $j>=0; $j--) {
  279. $f2[$j] = residue_sub($f2[$j], residue_mul($u0, $f2[$j + 2]));
  280. $f2[$j + 1] = residue_sub($f2[$j + 1], residue_mul($u1, $f2[$j + 2]));
  281. $f2[$j + 2] = 0;
  282. }
  283. // v = v0 + v1*x
  284. // u | (v0^2 - f0) + (2*v0*v1 - f1)*x + v1^2*x^2 = u0*v1^2 + u1*v1^2*x + v1^2*x^2
  285. // v0^2 - f0 = u0*v1^2
  286. // 2*v0*v1 - f1 = u1*v1^2
  287. // v0^2 = f0 + u0*v1^2 = (f1 + u1*v1^2)^2 / (2*v1)^2
  288. // (f1^2) + 2*(f1*u1-2*f0) * v1^2 + (u1^2-4*u0) * v1^4 = 0
  289. // v1^2 = ((2*f0-f1*u1) +- 2*sqrt(-f0*f1*u1 + f0^2 + f1^2*u0))) / (u1^2-4*u0)
  290. $f0 = $f2[0];
  291. $f1 = $f2[1];
  292. $u0double = residue_add($u0, $u0);
  293. $coeff2 = residue_sub(residue_mul($u1, $u1), residue_add($u0double, $u0double));
  294. $coeff1 = residue_sub(residue_add($f0, $f0), residue_mul($f1, $u1));
  295. if (gmp_cmp($coeff2, 0) == 0) {
  296. if (gmp_cmp($coeff1, 0) == 0) {
  297. if (gmp_cmp($f1, 0) == 0) {
  298. // impossible
  299. //printf("bad f(), double root detected\n");
  300. }
  301. return 0;
  302. }
  303. $sqr = residue_mul(residue_mul($f1, $f1), residue_inv(residue_add($coeff1, $coeff1)));
  304. $v1 = residue_sqrt($sqr);
  305. if (gmp_cmp($v1, $BAD) == 0)
  306. return 0;
  307. } else {
  308. $d = residue_add(residue_mul($f0, $f0), residue_mul($f1, residue_sub(residue_mul($f1, $u0), residue_mul($f0, $u1))));
  309. $d = residue_sqrt($d);
  310. if (gmp_cmp($d, $BAD) == 0)
  311. return 0;
  312. $d = residue_add($d, $d);
  313. $inv = residue_inv($coeff2);
  314. $root = residue_mul(residue_add($coeff1, $d), $inv);
  315. $v1 = residue_sqrt($root);
  316. if (gmp_cmp($v1, $BAD) == 0) {
  317. $root = residue_mul(residue_sub($coeff1, $d), $inv);
  318. $v1 = residue_sqrt($root);
  319. if (gmp_cmp($v1, $BAD) == 0)
  320. return 0;
  321. }
  322. }
  323. $v0 = residue_mul(residue_add($f1, residue_mul($u1, residue_mul($v1, $v1))), residue_inv(residue_add($v1, $v1)));
  324. $d->u[0] = $u0;
  325. $d->u[1] = $u1;
  326. $d->v[0] = $v0;
  327. $d->v[1] = $v1;
  328. return 1;
  329. }
  330. // UNTESTED
  331. function polynomial_mul($adeg, $a, $bdeg, $b, $resultprevdeg, &$result) { //static int polynomial_mul(int adeg, const ui64 a[], int bdeg, const ui64 b[], int resultprevdeg, ui64 result[])
  332. // $a[], $b[], $result[] is uint64
  333. if ($adeg < 0 || $bdeg < 0)
  334. return $resultprevdeg;
  335. // int i, j;
  336. $i = 0;
  337. $j = 0;
  338. for ($i = $resultprevdeg + 1; $i <= $adeg + $bdeg; $i++)
  339. $result[$i] = gmp_init(0);
  340. $resultprevdeg = $i - 1;
  341. for ($i = 0; $i <= $adeg; $i++)
  342. for ($j = 0; $j <= $bdeg; $j++)
  343. $result[$i + $j] = residue_add($result[$i + $j], residue_mul($a[$i], $b[$j]));
  344. while ($resultprevdeg >= 0 && gmp_cmp($result[$resultprevdeg],0) == 0)
  345. --$resultprevdeg;
  346. return $resultprevdeg;
  347. }
  348. // UNTESTED
  349. function polynomial_div_monic($adeg, &$a, $bdeg, $b, &$quotient) { //static int polynomial_div_monic(int adeg, ui64 a[], int bdeg, const ui64 b[], ui64* quotient)
  350. // $a[], $b[], $quotient are uint64
  351. //assert(bdeg >= 0);
  352. //assert(b[bdeg] == 1);
  353. //int i, j;
  354. //echo("bdeg: $bdeg\n");
  355. //debug_print_backtrace();
  356. $i = 0;
  357. $j = 0;
  358. for ($i = ($adeg - $bdeg); $i >= 0; $i--) {
  359. $q = $a[$i + $bdeg]; // uint64
  360. //if (gmp_cmp($quotient[0],0)>0)
  361. if(gettype($quotient)=="array")
  362. $quotient[$i] = $q;
  363. for ($j = 0; $j < $bdeg; $j++)
  364. $a[$i + $j] = residue_sub($a[$i + $j], residue_mul($q, $b[$j]));
  365. $a[$i + $j] = gmp_init(0);
  366. }
  367. $i += $bdeg;
  368. while ($i >= 0 && gmp_cmp($a[$i], 0) == 0)
  369. $i--;
  370. return $i;
  371. }
  372. // UNTESTED
  373. function polynomial_xgcd($adeg, $a, $bdeg, $b, &$pgcddeg, &$gcd, &$pmult1deg, &$mult1, &$pmult2deg, &$mult2) { //static void polynomial_xgcd(int adeg, const ui64 a[3], int bdeg, const ui64 b[3], int* pgcddeg, ui64 gcd[3], int* pmult1deg, ui64 mult1[3], int* pmult2deg, ui64 mult2[3])
  374. // $a[], $b[], $gcd[], $mult1[], $mult2[] are uint64
  375. $sdeg = -1;
  376. //ui64 s[3] = {0, 0, 0};
  377. $s = Array(gmp_init(0),gmp_init(0),gmp_init(0));
  378. $mult1deg = 0;
  379. $mult1[0] = gmp_init(1);
  380. $mult1[1] = gmp_init(0);
  381. $mult1[2] = gmp_init(0);
  382. $tdeg = 0;
  383. //ui64 t[3] = {1, 0, 0};
  384. $t = Array(gmp_init(1),gmp_init(0),gmp_init(0));
  385. $mult2deg = -1;
  386. $mult2[0] = gmp_init(0);
  387. $mult2[1] = gmp_init(0);
  388. $mult2[2] = gmp_init(0);
  389. $rdeg = $bdeg;
  390. //ui64 r[3] = {b[0], b[1], b[2]};
  391. $r = Array($b[0],$b[1],$b[2]);
  392. $gcddeg = $adeg;
  393. $gcd[0] = $a[0];
  394. $gcd[1] = $a[1];
  395. $gcd[2] = $a[2];
  396. // s*u1 + t*u2 = r
  397. // mult1*u1 + mult2*u2 = gcd
  398. while ($rdeg >= 0) {
  399. if ($rdeg > $gcddeg) {
  400. //unsigned tmp;
  401. $tmp = 0;
  402. //int tmpi;
  403. $tmpi = 0;
  404. $tmp = $rdeg; $rdeg = $gcddeg; $gcddeg = $tmp;
  405. $tmpi = $sdeg; $sdeg = $mult1deg; $mult1deg = $tmpi;
  406. $tmpi = $tdeg; $tdeg = $mult2deg; $mult2deg = $tmpi;
  407. //ui64 tmp2;
  408. $tmp2 = gmp_init(0);
  409. $tmp2 = $r[0]; $r[0] = $gcd[0]; $gcd[0] = $tmp2;
  410. $tmp2 = $r[1]; $r[1] = $gcd[1]; $gcd[1] = $tmp2;
  411. $tmp2 = $r[2]; $r[2] = $gcd[2]; $gcd[2] = $tmp2;
  412. $tmp2 = $s[0]; $s[0] = $mult1[0]; $mult1[0] = $tmp2;
  413. $tmp2 = $s[1]; $s[1] = $mult1[1]; $mult1[1] = $tmp2;
  414. $tmp2 = $s[2]; $s[2] = $mult1[2]; $mult1[2] = $tmp2;
  415. $tmp2 = $t[0]; $t[0] = $mult2[0]; $mult2[0] = $tmp2;
  416. $tmp2 = $t[1]; $t[1] = $mult2[1]; $mult2[1] = $tmp2;
  417. $tmp2 = $t[2]; $t[2] = $mult2[2]; $mult2[2] = $tmp2;
  418. continue;
  419. }
  420. //int delta = gcddeg - rdeg;
  421. $delta = $gcddeg - $rdeg;
  422. $mult = residue_mul($gcd[$gcddeg], residue_inv($r[$rdeg])); // ui64
  423. // quotient = mult * x**delta
  424. //assert(rdeg + delta < 3);
  425. for ($i = 0; $i <= $rdeg; $i++)
  426. $gcd[$i + $delta] = residue_sub($gcd[$i + $delta], residue_mul($mult, $r[$i]));
  427. while ($gcddeg >= 0 && gmp_cmp($gcd[$gcddeg], 0) == 0)
  428. $gcddeg--;
  429. //assert(sdeg + delta < 3);
  430. for ($i = 0; $i <= $sdeg; $i++)
  431. $mult1[$i + $delta] = residue_sub($mult1[$i + $delta], residue_mul($mult, $s[$i]));
  432. if ($mult1deg < $sdeg + $delta)
  433. $mult1deg = $sdeg + $delta;
  434. while ($mult1deg >= 0 && gmp_cmp($mult1[$mult1deg],0) == 0)
  435. $mult1deg--;
  436. //assert(tdeg + delta < 3);
  437. for ($i = 0; $i <= $tdeg; $i++)
  438. $mult2[$i + $delta] = residue_sub($mult2[$i + $delta], residue_mul($mult, $t[$i]));
  439. if ($mult2deg < $tdeg + $delta)
  440. $mult2deg = $tdeg + $delta;
  441. while ($mult2deg >= 0 && gmp_cmp($mult2[$mult2deg], 0) == 0)
  442. $mult2deg--;
  443. }
  444. // d1 = gcd, e1 = mult1, e2 = mult2
  445. $pgcddeg = $gcddeg;
  446. $pmult1deg = $mult1deg;
  447. $pmult2deg = $mult2deg;
  448. }
  449. // UNTESTED
  450. function u2poly(&$src, &$polyu, &$polyv) { //static int u2poly(const TDivisor* src, ui64 polyu[3], ui64 polyv[2])
  451. // $polyu[] and $polyv[] are uint64
  452. global $BAD;
  453. if (gmp_cmp($src->u[1], $BAD) != 0) {
  454. $polyu[0] = $src->u[0];
  455. $polyu[1] = $src->u[1];
  456. $polyu[2] = 1;
  457. $polyv[0] = $src->v[0];
  458. $polyv[1] = $src->v[1];
  459. return 2;
  460. }
  461. if (gmp_cmp($src->u[0], $BAD) != 0) {
  462. $polyu[0] = $src->u[0];
  463. $polyu[1] = 1;
  464. $polyv[0] = $src->v[0];
  465. $polyv[1] = 0;
  466. return 1;
  467. }
  468. $polyu[0] = gmp_init(1);
  469. $polyv[0] = gmp_init(0);
  470. $polyv[1] = gmp_init(0);
  471. return 0;
  472. }
  473. // UNTESTED
  474. function divisor_add($src1, $src2, &$dst) {// static void divisor_add(const TDivisor* src1, const TDivisor* src2, TDivisor* dst)
  475. //ui64 u1[3], u2[3], v1[2], v2[2];
  476. global $f, $BAD;
  477. $gmp0 = gmp_init(0);
  478. $u1 = Array($gmp0,$gmp0,$gmp0);
  479. $u2 = Array($gmp0,$gmp0,$gmp0);
  480. $v1 = Array($gmp0,$gmp0);
  481. $v2 = Array($gmp0,$gmp0);
  482. $u1deg = u2poly($src1, $u1, $v1); // int
  483. $u2deg = u2poly($src2, $u2, $v2); // int
  484. //debug_print_backtrace();
  485. // extended gcd: d1 = gcd(u1, u2) = e1*u1 + e2*u2
  486. //int d1deg, e1deg, e2deg;
  487. $d1deg = 0;
  488. $e1deg = 0;
  489. $e2deg = 0;
  490. //ui64 d1[3], e1[3], e2[3];
  491. $d1 = Array($gmp0,$gmp0,$gmp0);
  492. $e1 = Array($gmp0,$gmp0,$gmp0);
  493. $e2 = Array($gmp0,$gmp0,$gmp0);
  494. polynomial_xgcd($u1deg, $u1, $u2deg, $u2, $d1deg, $d1, $e1deg, $e1, $e2deg, $e2);
  495. //assert(e1deg <= 1);
  496. //assert(e2deg <= 1);
  497. // extended gcd again: d = gcd(d1, v1+v2) = c1*d1 + c2*(v1+v2)
  498. //ui64 b[3] = {residue_add(v1[0], v2[0]), residue_add(v1[1], v2[1]), 0};
  499. $b = Array(residue_add($v1[0], $v2[0]), residue_add($v1[1], $v2[1]), gmp_init(0));
  500. //int bdeg = (b[1] == 0 ? (b[0] == 0 ? -1 : 0) : 1);
  501. $bdeg = 1;
  502. if(gmp_cmp($b[1], 0) == 0) {
  503. if(gmp_cmp($b[0], 0) == 0) {
  504. $bdeg = -1;
  505. } else {
  506. $bdeg = 0;
  507. }
  508. }
  509. //for($i=0;$i<3;$i++) {
  510. // echo("b[$i]: ".strval($b[$i])."\n");
  511. //}
  512. //int ddeg, c1deg, c2deg;
  513. $ddeg = 0;
  514. $c1deg = 0;
  515. $c2deg = 0;
  516. //ui64 d[3], c1[3], c2[3];
  517. $d = Array($gmp0,$gmp0,$gmp0);
  518. $c1 = Array($gmp0,$gmp0,$gmp0);
  519. $c2 = Array($gmp0,$gmp0,$gmp0);
  520. polynomial_xgcd($d1deg, $d1, $bdeg, $b, $ddeg, $d, $c1deg, $c1, $c2deg, $c2);
  521. //echo("ddeg: $u1deg\n");
  522. //echo("c1deg: $c1deg\n");
  523. //echo("c2deg: $c2deg\n");
  524. /*
  525. for($i=0;$i<3;$i++) {
  526. echo("d[$i]: ".strval($d[$i])."\n");
  527. }
  528. for($i=0;$i<3;$i++) {
  529. echo("c1[$i]: ".strval($c1[$i])."\n");
  530. }
  531. for($i=0;$i<3;$i++) {
  532. echo("c2[$i]: ".strval($c2[$i])."\n");
  533. }*/
  534. //assert(c1deg <= 0);
  535. //assert(c2deg <= 1);
  536. //assert(ddeg >= 0);
  537. $dmult = residue_inv($d[$ddeg]); // ui64
  538. for ($i = 0; $i < $ddeg; $i++)
  539. $d[$i] = residue_mul($d[$i], $dmult);
  540. $d[$i] = 1;
  541. for ($i = 0; $i <= $c1deg; $i++)
  542. $c1[$i] = residue_mul($c1[$i], $dmult);
  543. for ($i = 0; $i <= $c2deg; $i++)
  544. $c2[$i] = residue_mul($c2[$i], $dmult);
  545. //ui64 u[5];
  546. $u = Array($gmp0,$gmp0,$gmp0,$gmp0,$gmp0);
  547. // echo("u1deg, u2deg: $u1deg $u2deg\n");
  548. // for($i=0;$i<3;$i++) {
  549. // echo("u1[$i]: ".strval($u1[$i])."\n");
  550. // }
  551. // for($i=0;$i<3;$i++) {
  552. // echo("u2[$i]: ".strval($u2[$i])."\n");
  553. // }
  554. $udeg = polynomial_mul($u1deg, $u1, $u2deg, $u2, -1, $u); // int
  555. // for($i=0;$i<5;$i++) {
  556. // echo("u[$i]: ".strval($u[$i])."\n");
  557. // }
  558. // echo("udeg: $udeg\n");
  559. // u is monic
  560. //ui64 v[7], tmp[7];
  561. $v = Array($gmp0,$gmp0,$gmp0,$gmp0,$gmp0,$gmp0,$gmp0);
  562. $tmp = Array($gmp0,$gmp0,$gmp0,$gmp0,$gmp0,$gmp0,$gmp0);
  563. //int vdeg, tmpdeg;
  564. $vdeg = 0;
  565. $tmpdeg = 0;
  566. // c1*(e1*u1*v2 + e2*u2*v1) + c2*(v1*v2 + f)
  567. // c1*(e1*u1*(v2-v1) + d1*v1) + c2*(v1*v2 + f)
  568. $v[0] = residue_sub($v2[0], $v1[0]);
  569. $v[1] = residue_sub($v2[1], $v1[1]);
  570. $tmpdeg = polynomial_mul($e1deg, $e1, 1, $v, -1, $tmp);
  571. $vdeg = polynomial_mul($u1deg, $u1, $tmpdeg, $tmp, -1, $v);
  572. $vdeg = polynomial_mul($d1deg, $d1, 1, $v1, $vdeg, $v);
  573. for ($i = 0; $i <= $vdeg; $i++)
  574. $v[$i] = residue_mul($v[$i], $c1[0]);
  575. //memcpy(tmp, f, 6 * sizeof(f[0]));
  576. for($iii = 0; $iii < 6; $iii++) {
  577. $tmp[$iii] = $f[$iii];
  578. }
  579. $tmpdeg = 5;
  580. $tmpdeg = polynomial_mul(1, $v1, 1, $v2, $tmpdeg, $tmp);
  581. $vdeg = polynomial_mul($c2deg, $c2, $tmpdeg, $tmp, $vdeg, $v);
  582. //echo("udeg, ddeg: $udeg $ddeg\n");
  583. if ($ddeg > 0) {
  584. //assert(udeg >= 2*ddeg);
  585. //ui64 udiv[5];
  586. $udiv = Array($gmp0,$gmp0,$gmp0,$gmp0,$gmp0);
  587. polynomial_div_monic($udeg, $u, $ddeg, $d, $udiv); $udeg -= $ddeg;
  588. polynomial_div_monic($udeg, $udiv, $ddeg, $d, $u); $udeg -= $ddeg;
  589. if ($vdeg >= 0) {
  590. //assert(vdeg >= ddeg);
  591. polynomial_div_monic($vdeg, $v, $ddeg, $d, $udiv); $vdeg -= $ddeg;
  592. //memcpy(v, udiv, (vdeg + 1) * sizeof(v[0])); what the?
  593. for($iii = 0; $iii < ($vdeg + 1); $iii++) {
  594. $v[$iii] = $udiv[$iii];
  595. }
  596. }
  597. }
  598. $empty = gmp_init(0); // NULL placeholder
  599. $vdeg = polynomial_div_monic($vdeg, $v, $udeg, $u, $empty);
  600. while ($udeg > 2) {
  601. //assert(udeg <= 4);
  602. //assert(vdeg <= 3);
  603. // u' = monic((f-v^2)/u), v'=-v mod u'
  604. $tmpdeg = polynomial_mul($vdeg, $v, $vdeg, $v, -1, $tmp);
  605. for ($i = 0; $i <= $tmpdeg && $i <= 5; $i++)
  606. $tmp[$i] = residue_sub($f[$i], $tmp[$i]);
  607. for (; $i <= $tmpdeg; $i++)
  608. $tmp[$i] = residue_sub(0, $tmp[$i]);
  609. for (; $i <= 5; $i++)
  610. $tmp[$i] = $f[$i];
  611. $tmpdeg = $i - 1;
  612. $udiv = Array($gmp0,$gmp0,$gmp0,$gmp0,$gmp0);
  613. polynomial_div_monic($tmpdeg, $tmp, $udeg, $u, $udiv);
  614. $udeg = $tmpdeg - $udeg;
  615. $mult = residue_inv($udiv[$udeg]); // uint64
  616. for ($i = 0; $i < $udeg; $i++)
  617. $u[$i] = residue_mul($udiv[$i], $mult);
  618. $u[$i] = 1;
  619. for ($i = 0; $i <= $vdeg; $i++)
  620. $v[$i] = residue_sub(0, $v[$i]);
  621. $empty = 0;
  622. $vdeg = polynomial_div_monic($vdeg, $v, $udeg, $u, $empty);
  623. }
  624. if ($udeg == 2) {
  625. $dst->u[0] = $u[0];
  626. $dst->u[1] = $u[1];
  627. //$dst->v[0] = (vdeg >= 0 ? v[0] : 0);
  628. if($vdeg>=0) {
  629. $dst->v[0] = $v[0];
  630. } else {
  631. $dst->v[0] = 0;
  632. }
  633. //$dst->v[1] = (vdeg >= 1 ? v[1] : 0);
  634. if($vdeg>=1) {
  635. $dst->v[1] = $v[1];
  636. } else {
  637. $dst->v[1] = 0;
  638. }
  639. } else if ($udeg == 1) {
  640. $dst->u[0] = $u[0];
  641. $dst->u[1] = $BAD;
  642. //$dst->v[0] = (vdeg >= 0 ? v[0] : 0);
  643. if($vdeg>=0) {
  644. $dst->v[0] = $v[0];
  645. } else {
  646. $dst->v[0] = 0;
  647. }
  648. $dst->v[1] = $BAD;
  649. } else {
  650. //assert(udeg == 0);
  651. $dst->u[0] = $BAD;
  652. $dst->u[1] = $BAD;
  653. $dst->v[0] = $BAD;
  654. $dst->v[1] = $BAD;
  655. }
  656. }
  657. // UNTESTED
  658. // #define divisor_double(src, dst) divisor_add(src, src, dst)
  659. function divisor_double($src, &$dst) {
  660. return divisor_add($src, $src, $dst);
  661. }
  662. // UNTESTED
  663. function divisor_mul(&$src, $mult, &$dst) { //static void divisor_mul(const TDivisor* src, ui64 mult, TDivisor* dst)
  664. // mult is uint64
  665. global $BAD;
  666. if (gmp_cmp($mult, 0) == 0) {
  667. $dst->u[0] = $BAD;
  668. $dst->u[1] = $BAD;
  669. $dst->v[0] = $BAD;
  670. $dst->v[1] = $BAD;
  671. return;
  672. }
  673. //TDivisor cur = *src; this defines cur and copies src to cur
  674. $cur = new TDivisor();
  675. $cur->u = Array($src->u[0],$src->u[1]);
  676. $cur->v = Array($src->v[0],$src->v[1]);
  677. while (!(gmp_cmp(gmp_and($mult, 1),0)>0)) {
  678. divisor_double($cur, $cur);
  679. $mult = uint64(gmp_shiftr($mult,1));
  680. }
  681. // *dst = cur; copy cur to dst
  682. $dst->u = Array($cur->u[0],$cur->u[1]);
  683. $dst->v = Array($cur->v[0],$cur->v[1]);
  684. $mult = uint64(gmp_shiftr($mult,1));
  685. while(gmp_cmp($mult, 0) != 0) {// while ((mult >>= 1) != 0) {
  686. divisor_double($cur, $cur);
  687. if (gmp_cmp(gmp_and($mult, 1), 0) > 0)
  688. divisor_add($dst, $cur, $dst);
  689. $mult = uint64(gmp_shiftr($mult,1));
  690. }
  691. }
  692. // UNTESTED
  693. function divisor_mul128($src, $mult_lo, $mult_hi, &$dst) { // static void divisor_mul128(const TDivisor* src, ui64 mult_lo, ui64 mult_hi, TDivisor* dst)
  694. // mult_lo and mult_hi are uint64
  695. global $BAD;
  696. if (gmp_cmp($mult_lo,0) == 0 && gmp_cmp($mult_hi, 0) == 0) {
  697. $dst->u[0] = $BAD;
  698. $dst->u[1] = $BAD;
  699. $dst->v[0] = $BAD;
  700. $dst->v[1] = $BAD;
  701. return;
  702. }
  703. //TDivisor cur = *src;
  704. $cur = new TDivisor();
  705. $cur->u = Array($src->u[0],$src->u[1]);
  706. $cur->v = Array($src->v[0],$src->v[1]);
  707. while (!(gmp_cmp(gmp_and($mult_lo, 1),0)>0)) {
  708. divisor_double($cur, $cur);
  709. $mult_lo = uint64(gmp_shiftr($mult_lo, 1));
  710. if (gmp_cmp(gmp_and($mult_hi, 1), 0) > 0)
  711. $mult_lo = uint64(gmp_or($mult_lo,uint64(gmp_shiftl(1,63))));
  712. $mult_hi = uint64(gmp_shiftr($mult_hi, 1));
  713. }
  714. // *dst = cur;
  715. $dst->u = Array($cur->u[0],$cur->u[1]);
  716. $dst->v = Array($cur->v[0],$cur->v[1]);
  717. for (;;) {
  718. $mult_lo = uint64(gmp_shiftr($mult_lo, 1));
  719. if (gmp_cmp(gmp_and($mult_hi, 1), 0) > 0)
  720. $mult_lo = uint64(gmp_or($mult_lo,uint64(gmp_shiftl(1,63))));
  721. $mult_hi = uint64(gmp_shiftr($mult_hi, 1));
  722. if (gmp_cmp($mult_lo, 0) == 0 && gmp_cmp($mult_hi, 0) == 0)
  723. break;
  724. divisor_double($cur, $cur);
  725. //echo("\nu0: " . gmp_strval($cur->u[0]));
  726. //echo("\nu1: " . gmp_strval($cur->u[1]));
  727. //echo("\nv0: " . gmp_strval($cur->v[0]));
  728. //echo("\nv1: " . gmp_strval($cur->v[1]) . "\n");
  729. if (gmp_cmp(gmp_and($mult_lo, 1), 0) > 0) {
  730. divisor_add($dst, $cur, $dst);
  731. }
  732. }
  733. }
  734. function rol($x, $shift) {
  735. //assert(shift > 0 && shift < 32);
  736. if(gettype($shift)=="object") {
  737. $shift = gmp_intval($shift);
  738. }
  739. return uint32(gmp_or(gmp_shiftl($x, $shift), gmp_shiftr($x, (32 - $shift))));
  740. }
  741. // array fun yay D: !!
  742. // UNTESTED
  743. function sha1_single_block($input, &$output) { //static void sha1_single_block(unsigned char input[64], unsigned char output[20])
  744. //unsigned a, b, c, d, e;
  745. $a = gmp_init("0x67452301");
  746. $b = gmp_init("0xEFCDAB89");
  747. $c = gmp_init("0x98BADCFE");
  748. $d = gmp_init("0x10325476");
  749. $e = gmp_init("0xC3D2E1F0");
  750. //unsigned w[80];
  751. $w = Array();
  752. for ($i = 0; $i < 16; $i++) { // ok
  753. $w[$i] = uint32(gmp_or(gmp_or(gmp_or(gmp_shiftl($input[4*$i], 24), gmp_shiftl($input[4*$i+1], 16)), gmp_shiftl($input[4*$i+2], 8)), $input[4*$i+3]));
  754. }
  755. for ($i = 16; $i < 80; $i++) { // ok
  756. $w[$i] = rol(gmp_xor(gmp_xor(gmp_xor($w[$i - 3], $w[$i - 8]), $w[$i - 14]), $w[$i - 16]), 1);
  757. //echo($w[$i] . " ");
  758. }
  759. for ($i = 0; $i < 20; $i++) { // ?
  760. // for ~b
  761. //echo("A STEP1: $a\n");
  762. // bitwse not on b
  763. $b_inv = gmp_intval($b);
  764. $b_inv = ~$b_inv;
  765. $b_inv = $b_inv & 0xFFFFFFFF;
  766. $b_inv = gmp_init($b_inv);
  767. //$tmp = rol(a, tmp) + ((b & c) | (~b & d)) + e + w[i] + 0x5A827999;
  768. $tmp = uint32(gmp_or(gmp_and($b,$c),gmp_and($b_inv,$d)));
  769. //echo("A STEP2: $tmp\n");
  770. $tmp = uint32(gmp_add($tmp, rol($a, 5)));
  771. //echo("A STEP3: $tmp\n");
  772. $tmp = uint32(gmp_add($tmp,$e));
  773. //echo("A STEP4: $tmp\n");
  774. $tmp = uint32(gmp_add($tmp,$w[$i]));
  775. //echo("A STEP5: $tmp\n");
  776. $tmp = uint32(gmp_add($tmp,gmp_init("0x5A827999")));
  777. //$tmp = uint32(gmp_add(gmp_add(gmp_add(rol($a, 5),gmp_or(gmp_and($b,$c),gmp_and($b_inv, $d))),$e),$w[$i]),gmp_init("0x5A827999"));
  778. //$tmp = uint32(gmp_add(uint32(gmp_add(uint32(gmp_add(gmp_add(rol($a, 5),gmp_or(gmp_and($b, $c), gmp_and($b_inv, $d))),$e)),$w[$i])),gmp_init("0x5A827999")) );
  779. $e = $d;
  780. $d = $c;
  781. $c = uint32(rol($b, 30));
  782. $b = $a;
  783. $a = $tmp;
  784. }
  785. // echo("\n");
  786. // echo("A: " . gmp_strval($a) . "\n");
  787. // echo("B: " . gmp_strval($b) . "\n");
  788. // echo("C: " . gmp_strval($c) . "\n");
  789. // echo("D: " . gmp_strval($d) . "\n");
  790. // echo("E: " . gmp_strval($e) . "\n");
  791. for ($i = 20; $i < 40; $i++) {
  792. //unsigned tmp = rol(a, 5) + (b ^ c ^ d) + e + w[i] + 0x6ED9EBA1;
  793. $tmp = uint32(gmp_add(gmp_add(gmp_add(gmp_add(rol($a, 5),gmp_xor(gmp_xor($b,$c),$d)),$e),$w[$i]),gmp_init("0x6ED9EBA1")));
  794. $e = $d;
  795. $d = $c;
  796. $c = rol($b, 30);
  797. $b = $a;
  798. $a = $tmp;
  799. }
  800. for ($i = 40; $i < 60; $i++) {
  801. //unsigned tmp = rol(a, 5) + ((b & c) | (b & d) | (c & d)) + e + w[i] + 0x8F1BBCDC;
  802. $tmp = uint32(gmp_add(gmp_add(gmp_add(gmp_add(rol($a,5),gmp_or(gmp_or(gmp_and($b, $c), gmp_and($b, $d)), gmp_and($c, $d))),$e),$w[$i]),gmp_init("0x8F1BBCDC")));
  803. $e = $d;
  804. $d = $c;
  805. $c = rol($b, 30);
  806. $b = $a;
  807. $a = $tmp;
  808. }
  809. for ($i = 60; $i < 80; $i++) {
  810. //unsigned tmp = rol(a, 5) + (b ^ c ^ d) + e + w[i] + 0xCA62C1D6;
  811. $tmp = uint32(gmp_add(gmp_add(gmp_add(gmp_add(rol($a,5), gmp_xor(gmp_xor($b, $c),$d)), $e), $w[$i]), gmp_init("0xCA62C1D6")));
  812. $e = $d;
  813. $d = $c;
  814. $c = rol($b, 30);
  815. $b = $a;
  816. $a = $tmp;
  817. }
  818. $a = uint32(gmp_add($a, gmp_init("0x67452301")));
  819. $b = uint32(gmp_add($b, gmp_init("0xEFCDAB89")));
  820. $c = uint32(gmp_add($c, gmp_init("0x98BADCFE")));
  821. $d = uint32(gmp_add($d, gmp_init("0x10325476")));
  822. $e = uint32(gmp_add($e, gmp_init("0xC3D2E1F0")));
  823. $output[0] = uint8(gmp_shiftr($a, 24)); $output[1] = uint8(gmp_shiftr($a, 16)); $output[2] = uint8(gmp_shiftr($a, 8)); $output[3] = uint8($a);
  824. $output[4] = uint8(gmp_shiftr($b, 24)); $output[5] = uint8(gmp_shiftr($b, 16)); $output[6] = uint8(gmp_shiftr($b, 8)); $output[7] = uint8($b);
  825. $output[8] = uint8(gmp_shiftr($c, 24)); $output[9] = uint8(gmp_shiftr($c, 16)); $output[10] = uint8(gmp_shiftr($c, 8)); $output[11] = uint8($c);
  826. $output[12] = uint8(gmp_shiftr($d, 24)); $output[13] = uint8(gmp_shiftr($d, 16)); $output[14] = uint8(gmp_shiftr($d, 8)); $output[15] = uint8($d);
  827. $output[16] = uint8(gmp_shiftr($e, 24)); $output[17] = uint8(gmp_shiftr($e, 16)); $output[18] = uint8(gmp_shiftr($e, 8)); $output[19] = uint8($e);
  828. }
  829. // UNTESTED
  830. function Mix(&$buffer, $bufSize, &$key, $keySize) { //static void Mix(unsigned char* buffer, size_t bufSize, const unsigned char* key, size_t keySize)
  831. //unsigned char sha1_input[64];
  832. $sha1_input = Array();
  833. //unsigned char sha1_result[20];
  834. $sha1_result = Array();
  835. $half = floor($bufSize / 2);
  836. //assert(half <= sizeof(sha1_result) && half + keySize <= sizeof(sha1_input) - 9);
  837. $external_counter = 0;
  838. for ($external_counter = 0; $external_counter < 4; $external_counter++) {
  839. //memset(sha1_input, 0, sizeof(sha1_input));
  840. for($iii=0;$iii<64;$iii++) {
  841. $sha1_input[$iii] = gmp_init(0);;
  842. }
  843. //memcpy(sha1_input, buffer + half, half);
  844. for($iii=0;$iii<$half;$iii++) {
  845. $sha1_input[$iii] = $buffer[$iii+$half];
  846. }
  847. //memcpy(sha1_input + half, key, keySize);
  848. for($iii=0;$iii<$keySize;$iii++) {
  849. $sha1_input[$iii + $half] = $key[$iii];
  850. }
  851. //sha1_input[half + keySize] = 0x80;
  852. $sha1_input[$half + $keySize] = gmp_init(0x80);
  853. //sha1_input[sizeof(sha1_input) - 1] = (half + keySize) * 8;
  854. $sha1_input[64 - 1] = uint8(gmp_mul(gmp_init(($half + $keySize)), 8));
  855. $sha1_input[64 - 2] = uint8(gmp_div(gmp_mul(gmp_init($half + $keySize), 8), 0x100));
  856. sha1_single_block($sha1_input, $sha1_result);
  857. for ($i = $half & ~3; $i < $half; $i++)
  858. $sha1_result[$i] = $sha1_result[$i + 4 - ($half & 3)];
  859. for ($i = 0; $i < $half; $i++) {
  860. $tmp = $buffer[$i + $half];
  861. $buffer[$i + $half] = gmp_xor($buffer[$i], $sha1_result[$i]);
  862. $buffer[$i] = $tmp;
  863. }
  864. }
  865. }
  866. // UNTESTED
  867. function Unmix(&$buffer, $bufSize, &$key, $keySize) { // static void Unmix(unsigned char* buffer, size_t bufSize, const unsigned char* key, size_t keySize)
  868. //unsigned char sha1_input[64];
  869. $sha1_input = Array();
  870. //unsigned char sha1_result[20];
  871. $sha1_result = Array();
  872. $half = floor($bufSize / 2);
  873. //assert(half <= sizeof(sha1_result) && half + keySize <= sizeof(sha1_input) - 9);
  874. for ($external_counter = 0; $external_counter < 4; $external_counter++) {
  875. //memset(sha1_input, 0, sizeof(sha1_input));
  876. for($iii=0;$iii<64;$iii++) {
  877. $sha1_input[$iii] = gmp_init(0);
  878. }
  879. //memcpy(sha1_input, buffer, half);
  880. for($iii=0;$iii<$half;$iii++) {
  881. $sha1_input[$iii] = $buffer[$iii];
  882. }
  883. //memcpy(sha1_input + half, key, keySize);
  884. for($iii=0;$iii<$keySize;$iii++) {
  885. $sha1_input[$iii+$half] = $key[$iii];
  886. }
  887. $sha1_input[$half + $keySize] = gmp_init(0x80);
  888. $sha1_input[64 - 1] = uint8(gmp_mul(gmp_init(($half + $keySize)), 8));
  889. $sha1_input[64 - 2] = uint8(gmp_div(gmp_mul(gmp_init($half + $keySize), 8), 0x100));
  890. sha1_single_block($sha1_input, $sha1_result);
  891. for ($i = $half & ~3; $i < $half; $i++) {
  892. $sha1_result[$i] = $sha1_result[$i + 4 - ($half & 3)];
  893. }
  894. for ($i = 0; $i < $half; $i++) {
  895. $tmp = $buffer[$i];
  896. $buffer[$i] = gmp_xor($buffer[$i + $half], $sha1_result[$i]);
  897. $buffer[$i + $half] = $tmp;
  898. }
  899. }
  900. }
  901. $ERR_TOO_SHORT = 1;
  902. $ERR_TOO_LARGE = 2;
  903. $ERR_INVALID_CHARACTER = 3;
  904. $ERR_INVALID_CHECK_DIGIT = 4;
  905. $ERR_UNKNOWN_VERSION = 5;
  906. $ERR_UNLUCKY = 6;
  907. // UNTESTED
  908. function generate($installation_id_str, &$confirmation_id) { // static int generate(const CHARTYPE* installation_id_str, CHARTYPE confirmation_id[49])
  909. global $ERR_TOO_SHORT, $ERR_TOO_LARGE, $ERR_INVALID_CHARACTER, $ERR_INVALID_CHECK_DIGIT, $ERR_UNKNOWN_VERSION, $ERR_UNLUCKY, $NON_RESIDUE, $MOD, $BAD;
  910. $installation_id = Array(); //unsigned char installation_id[19]; // 10**45 < 256**19
  911. $installation_id_len = 0; //size_t installation_id_len = 0;
  912. $p = $installation_id_str;
  913. $count = 0;
  914. $totalCount = 0;
  915. $check = 0; // unsigned
  916. $plen = strlen($p);
  917. for($pi=0;$pi<$plen;$pi++) { //for (; *p; p++) {
  918. $pp = $p[$pi];
  919. if ($pp == ' ' || $pp == '-')
  920. continue;
  921. /*
  922. int d = *p - '0';
  923. if (d < 0 || d > 9)
  924. return ERR_INVALID_CHARACTER;
  925. */
  926. if(!is_numeric($pp)) {
  927. return $ERR_INVALID_CHARACTER;
  928. }
  929. $d = intval($pp);
  930. if ($count == 5 || ord($p[$pi+1]) == 0) {
  931. if (!$count)
  932. return ($totalCount == 45) ? $ERR_TOO_LARGE : $ERR_TOO_SHORT;
  933. if ($d != $check % 7)
  934. return ($count < 5) ? $ERR_TOO_SHORT : $ERR_INVALID_CHECK_DIGIT;
  935. $check = 0;
  936. $count = 0;
  937. continue;
  938. }
  939. $check += ($count % 2 ? $d * 2 : $d);
  940. $count++;
  941. $totalCount++;
  942. if ($totalCount > 45)
  943. return $ERR_TOO_LARGE;
  944. $carry = uint8(gmp_init($d)); // unsigned char
  945. for ($i = 0; $i < $installation_id_len; $i++) {
  946. $x = uint32(gmp_add(gmp_mul(gmp_init($installation_id[$i]), 10), $carry)); // unsigned
  947. $installation_id[$i] = gmp_intval(gmp_and($x, 0xFF));
  948. $carry = gmp_intval(uint8(gmp_shiftr($x, 8)));
  949. }
  950. if (gmp_cmp($carry, 0) > 0) {
  951. //assert(installation_id_len < sizeof(installation_id));
  952. $installation_id[$installation_id_len++] = gmp_intval($carry);
  953. }
  954. }
  955. if ($totalCount != 41 && $totalCount < 45)
  956. return $ERR_TOO_SHORT;
  957. for (; $installation_id_len < 19; $installation_id_len++)
  958. $installation_id[$installation_id_len] = 0;
  959. //static const unsigned char iid_key[4] = {0x6A, 0xC8, 0x5E, 0xD4};
  960. $iid_key = Array(gmp_init(0x6A), gmp_init(0xC8), gmp_init(0x5E), gmp_init(0xD4));
  961. // convert instalation id to gmp
  962. for($iii=0;$iii<19;$iii++) {
  963. $installation_id[$iii] = uint8(gmp_init($installation_id[$iii]));
  964. }
  965. Unmix($installation_id, $totalCount == 41 ? 17 : 19, $iid_key, 4);
  966. // convert it back to num (no need)
  967. //for($iii=0;$iii<19;$iii++) {
  968. // $installation_id[$iii] = gmp_intval($installation_id[$iii]);
  969. //}
  970. if (gmp_cmp($installation_id[18], 0x10) >= 0)
  971. return $ERR_UNKNOWN_VERSION;
  972. // #pragma pack(push, 1)
  973. /*
  974. struct {
  975. ui64 HardwareID;
  976. ui64 ProductIDLow;
  977. unsigned char ProductIDHigh;
  978. unsigned short KeySHA1;
  979. } parsed;
  980. */
  981. $parsed = new stdClass();
  982. $parsed->HardwareID = gmp_init(0); // uint64
  983. $parsed->ProductIDLow = gmp_init(0); // uint64
  984. $parsed->ProductIDHigh = gmp_init(0); // uint8
  985. $parsed->KeySHA1 = gmp_init(0); // uint16
  986. // #pragma pack(pop)
  987. //memcpy(&parsed, installation_id, sizeof(parsed));
  988. // splitted into multiple lines to not make very long lines
  989. $parsed->HardwareID = gmp_or(gmp_or(gmp_or($installation_id[0],gmp_shiftl($installation_id[1],8)),gmp_shiftl($installation_id[2],16)),gmp_shiftl($installation_id[3],24));
  990. $parsed->HardwareID = gmp_or($parsed->HardwareID,gmp_or(gmp_or(gmp_or(gmp_shiftl($installation_id[4],32),gmp_shiftl($installation_id[5],40)),gmp_shiftl($installation_id[6],48)),gmp_shiftl($installation_id[7],56)));
  991. $parsed->ProductIDLow = gmp_or(gmp_or(gmp_or($installation_id[8],gmp_shiftl($installation_id[9],8)),gmp_shiftl($installation_id[10],16)),gmp_shiftl($installation_id[11],24));
  992. $parsed->ProductIDLow = gmp_or($parsed->ProductIDLow,gmp_or(gmp_or(gmp_or(gmp_shiftl($installation_id[12],32),gmp_shiftl($installation_id[13],40)),gmp_shiftl($installation_id[14],48)),gmp_shiftl($installation_id[15],56)));
  993. $parsed->ProductIDHigh = $installation_id[16];
  994. $parsed->KeySHA1 = gmp_or($installation_id[17], gmp_shiftl($installation_id[18],8));
  995. // end of memcpy
  996. $productId1 = uint32(gmp_and($parsed->ProductIDLow, gmp_init((1 << 17) - 1))); // unsigned
  997. $productId2 = uint32(gmp_and(gmp_shiftr($parsed->ProductIDLow, 17), gmp_init((1 << 10) - 1))); // unsigned
  998. $productId3 = uint32(gmp_and(gmp_shiftr($parsed->ProductIDLow, 27), gmp_init((1 << 25) - 1))); // unsigned
  999. $version = uint32(gmp_and(gmp_shiftr($parsed->ProductIDLow, 52), 7)); // unsigned
  1000. $productId4 = uint32(gmp_or(gmp_shiftr($parsed->ProductIDLow, 55), gmp_shiftl($parsed->ProductIDHigh, 9))); // unsigned
  1001. if (gmp_cmp($version, gmp_init($totalCount == 41 ? 4 : 5))!=0)
  1002. return $ERR_UNKNOWN_VERSION;
  1003. //printf("Product ID: %05u-%03u-%07u-%05u\n", productId1, productId2, productId3, productId4);
  1004. $keybuf = Array(); //unsigned char keybuf[16]; // unsigned char
  1005. //memcpy(keybuf, &parsed.HardwareID, 8);
  1006. // parsed->HardwareID is basically $installation_id[0 .. 7]
  1007. for($iii=0;$iii<8;$iii++) {
  1008. $keybuf[$iii] = $installation_id[$iii];
  1009. }
  1010. $productIdMixed = gmp_or(gmp_or(gmp_or(uint64(gmp_shiftl($productId1, 41)), uint64(gmp_shiftl($productId2, 58))), uint64(gmp_shiftl($productId3, 17))), uint64($productId4)); // uint64
  1011. //memcpy(keybuf + 8, &productIdMixed, 8);
  1012. // 0x1122334455667788 -> array[0] = 0x88, array[1] = 0x77 and on...
  1013. for($iii=0;$iii<8;$iii++) {
  1014. $keybuf[$iii+8] = uint8(gmp_and(gmp_shiftr($productIdMixed, $iii*8), gmp_init(0xFF)));
  1015. }
  1016. //TDivisor d;
  1017. $d = new TDivisor();
  1018. $d->u = Array(gmp_init(0),gmp_init(0));
  1019. $d->v = Array(gmp_init(0),gmp_init(0));
  1020. //$attempt - unsigned char
  1021. for ($attempt = 0; $attempt <= 0x80; $attempt++) {
  1022. //echo("attempt: $attempt\n");
  1023. /*union {
  1024. unsigned char buffer[14];
  1025. struct {
  1026. ui64 lo;
  1027. ui64 hi;
  1028. };
  1029. } u;
  1030. union makes char and the both ui64 be at the same memory
  1031. u.buffer[0] = 0x64 makes u.lo = 0x64
  1032. */
  1033. $u = new stdClass();
  1034. $u->lo = gmp_init(0);
  1035. $u->hi = gmp_init(0);
  1036. $u->buffer = Array();
  1037. for($iii=0;$iii<14;$iii++) {
  1038. $u->buffer[$iii] = gmp_init(0);
  1039. }
  1040. $u->buffer[7] = uint8(gmp_init($attempt));
  1041. Mix($u->buffer, 14, $keybuf, 16);
  1042. // copy buffer to lo and hi
  1043. $u->lo = gmp_or(gmp_or(gmp_or($u->buffer[0],gmp_shiftl($u->buffer[1],8)),gmp_shiftl($u->buffer[2],16)),gmp_shiftl($u->buffer[3],24));
  1044. $u->lo = gmp_or($u->lo,gmp_or(gmp_or(gmp_or(gmp_shiftl($u->buffer[4],32),gmp_shiftl($u->buffer[5],40)),gmp_shiftl($u->buffer[6],48)),gmp_shiftl($u->buffer[7],56)));
  1045. $u->hi = gmp_or(gmp_or(gmp_or($u->buffer[8],gmp_shiftl($u->buffer[9],8)),gmp_shiftl($u->buffer[10],16)),gmp_shiftl($u->buffer[11],24));
  1046. $u->hi = gmp_or($u->hi,gmp_or(gmp_shiftl($u->buffer[12],32),gmp_shiftl($u->buffer[13],40)));
  1047. $x2 = ui128_quotient_mod($u->lo, $u->hi); //ui64
  1048. $x1 = uint64(gmp_sub($u->lo, uint64(gmp_mul($x2, $MOD)))); // ui64
  1049. $x2 = uint64(gmp_add($x2, 1));//x2++;
  1050. $d->u[0] = residue_sub(residue_mul($x1, $x1), residue_mul($NON_RESIDUE, residue_mul($x2, $x2)));
  1051. $d->u[1] = residue_add($x1, $x1);
  1052. if (find_divisor_v($d))
  1053. break;
  1054. }
  1055. if ($attempt > 0x80)
  1056. return $ERR_UNLUCKY;
  1057. divisor_mul128($d, gmp_init("0x04e21b9d10f127c1"), gmp_init("0x40da7c36d44c"), $d);
  1058. /*union {
  1059. struct {
  1060. ui64 encoded_lo, encoded_hi;
  1061. };
  1062. struct {
  1063. uint32_t encoded[4];
  1064. };
  1065. } e;
  1066. */
  1067. $e = new stdClass();
  1068. $e->encoded_lo = gmp_init(0);
  1069. $e->encoded_hi = gmp_init(0);
  1070. $e->encoded = Array(gmp_init(0),gmp_init(0),gmp_init(0),gmp_init(0));
  1071. if ($d->u[0] == $BAD) {
  1072. // we can not get the zero divisor, actually...
  1073. $e->encoded_lo = __umul128(uint64(gmp_add($MOD, gmp_init(2))), $MOD, $e->encoded_hi);
  1074. } else if ($d->u[1] == $BAD) {
  1075. // O(1/MOD) chance
  1076. //encoded = (unsigned __int128)(MOD + 1) * d.u[0] + MOD; // * MOD + d.u[0] is fine too
  1077. $e->encoded_lo = __umul128(uint64(gmp_add($MOD, 1)), $d->u[0], $e->encoded_hi);
  1078. $e->encoded_lo = uint64(gmp_add($e->encoded_lo, $MOD));
  1079. if(gmp_cmp($e->encoded_lo, $MOD)<0) { // e.encoded_hi += (e.encoded_lo < MOD);
  1080. $e->encoded_hi = uint64(gmp_add($e->encoded_hi, 1));
  1081. }
  1082. } else {
  1083. //x1 = (d.u[1] % 2 ? d.u[1] + MOD : d.u[1]) / 2; // ui64
  1084. if(gmp_cmp(gmp_mod($d->u[1],gmp_init(2)),0)>0) {
  1085. $x1 = uint64(gmp_div(uint64(gmp_add($d->u[1], $MOD)),2));
  1086. } else {
  1087. $x1 = uint64(gmp_div($d->u[1],2));
  1088. }
  1089. $x2sqr = residue_sub(residue_mul($x1, $x1), $d->u[0]); // ui64
  1090. $x2 = residue_sqrt($x2sqr); // ui64
  1091. if (gmp_cmp($x2, $BAD) == 0) {
  1092. $x2 = residue_sqrt(residue_mul($x2sqr, residue_inv($NON_RESIDUE)));
  1093. //assert(x2 != BAD);
  1094. $e->encoded_lo = __umul128(uint64(gmp_add($MOD, 1)), uint64(gmp_add($MOD, $x2)), $e->encoded_hi);
  1095. $e->encoded_lo = uint64(gmp_add($e->encoded_lo, $x1));
  1096. if(gmp_cmp($e->encoded_lo, $x1)<0) { // e.encoded_hi += (e.encoded_lo < x1);
  1097. $e->encoded_hi = uint64(gmp_add($e->encoded_hi,1));
  1098. }
  1099. } else {
  1100. // points (-x1+x2, v(-x1+x2)) and (-x1-x2, v(-x1-x2))
  1101. // all ui64
  1102. $x1a = residue_sub($x1, $x2);
  1103. $y1 = residue_sub($d->v[0], residue_mul($d->v[1], $x1a));
  1104. $x2a = residue_add($x1, $x2);
  1105. $y2 = residue_sub($d->v[0], residue_mul($d->v[1], $x2a));
  1106. if (gmp_cmp($x1a, $x2a)>0) {
  1107. $tmp = $x1a; //ui64
  1108. $x1a = $x2a;
  1109. $x2a = $tmp;
  1110. }
  1111. //if ((y1 ^ y2) & 1) {
  1112. if(gmp_cmp(gmp_and(gmp_xor($y1, $y2), 1), 0)>0) {
  1113. $tmp = $x1a;
  1114. $x1a = $x2a;
  1115. $x2a = $tmp;
  1116. }
  1117. $e->encoded_lo = __umul128(uint64(gmp_add($MOD, 1)), $x1a, $e->encoded_hi);
  1118. $e->encoded_lo = uint64(gmp_add($e->encoded_lo, $x2a));
  1119. //e.encoded_hi += (e.encoded_lo < x2a);
  1120. if(gmp_cmp($e->encoded_lo, $x2a) < 0) {
  1121. $e->encoded_hi = uint64(gmp_add($e->encoded_hi, 1));
  1122. }
  1123. }
  1124. }
  1125. $decimal = Array();//unsigned char decimal[35];
  1126. // convert e->encoeded_lo and e->encoded_hi to e->encoded
  1127. $e->encoded[0] = gmp_and($e->encoded_lo,gmp_init("0xFFFFFFFF"));
  1128. $e->encoded[1] = gmp_and(gmp_shiftr($e->encoded_lo,32),gmp_init("0xFFFFFFFF"));
  1129. $e->encoded[2] = gmp_and($e->encoded_hi,gmp_init("0xFFFFFFFF"));
  1130. $e->encoded[3] = gmp_and(gmp_shiftr($e->encoded_hi,32),gmp_init("0xFFFFFFFF"));
  1131. for ($i = 0; $i < 35; $i++) {
  1132. $c = uint32(gmp_mod($e->encoded[3], 10)); // unsigned
  1133. $e->encoded[3] = uint32(gmp_div($e->encoded[3],10));
  1134. //unsigned c2 = ((ui64)c << 32 | e.encoded[2]) % 10;
  1135. $c2 = uint32(gmp_mod(gmp_or(uint64(gmp_shiftl($c,32)), $e->encoded[2]),10));
  1136. //e.encoded[2] = ((ui64)c << 32 | e.encoded[2]) / 10;
  1137. $e->encoded[2] = uint32(gmp_div(gmp_or(uint64(gmp_shiftl($c,32)), $e->encoded[2]),10));
  1138. //unsigned c3 = ((ui64)c2 << 32 | e.encoded[1]) % 10;
  1139. $c3 = uint32(gmp_mod(gmp_or(uint64(gmp_shiftl($c2,32)), $e->encoded[1]),10));
  1140. //e.encoded[1] = ((ui64)c2 << 32 | e.encoded[1]) / 10;
  1141. $e->encoded[1] = uint32(gmp_div(gmp_or(uint64(gmp_shiftl($c2,32)), $e->encoded[1]),10));
  1142. //unsigned c4 = ((ui64)c3 << 32 | e.encoded[0]) % 10;
  1143. $c4 = uint32(gmp_mod(gmp_or(uint64(gmp_shiftl($c3,32)), $e->encoded[0]),10));
  1144. //e.encoded[0] = ((ui64)c3 << 32 | e.encoded[0]) / 10;
  1145. $e->encoded[0] = uint32(gmp_div(gmp_or(uint64(gmp_shiftl($c3,32)), $e->encoded[0]),10));
  1146. $decimal[34 - $i] = uint8($c4);
  1147. }
  1148. //assert(e.encoded[0] == 0 && e.encoded[1] == 0 && e.encoded[2] == 0 && e.encoded[3] == 0);
  1149. //CHARTYPE* q = confirmation_id;
  1150. $q = $confirmation_id;
  1151. $qi = 0;
  1152. // convert $decimal gmp to int
  1153. for($iii=0;$iii<35;$iii++) {
  1154. $decimal[$iii]=gmp_intval($decimal[$iii]);
  1155. }
  1156. for ($i = 0; $i < 7; $i++) {
  1157. if ($i) { //*q++ = '-';
  1158. $q[$qi] = '-';
  1159. $qi++;
  1160. }
  1161. $pi = $i*5;
  1162. /*unsigned char* p = decimal + i*5;
  1163. q[0] = p[0] + '0';
  1164. q[1] = p[1] + '0';
  1165. q[2] = p[2] + '0';
  1166. q[3] = p[3] + '0';
  1167. q[4] = p[4] + '0';
  1168. */
  1169. $zero = ord('0');
  1170. $q[$qi+0] = chr($decimal[$pi+0] + $zero);
  1171. $q[$qi+1] = chr($decimal[$pi+1] + $zero);
  1172. $q[$qi+2] = chr($decimal[$pi+2] + $zero);
  1173. $q[$qi+3] = chr($decimal[$pi+3] + $zero);
  1174. $q[$qi+4] = chr($decimal[$pi+4] + $zero);
  1175. //q[5] = ((p[0]+p[1]*2+p[2]+p[3]*2+p[4]) % 7) + '0';
  1176. $q[$qi+5] = chr((($decimal[$pi+0]+$decimal[$pi+1]*2+$decimal[$pi+2]+$decimal[$pi+3]*2+$decimal[$pi+4]) % 7) + $zero);
  1177. //q += 6;
  1178. $qi = $qi + 6;
  1179. }
  1180. //*q++ = 0;
  1181. $qi++;
  1182. //$q[$qi] = 0;
  1183. // copy $q to $confirmation_id
  1184. $confirmation_id = $q;
  1185. return 0;
  1186. }
  1187. ?>