1 // Hyperbolic Rogue -- basic utility functions
2 // Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details
3 
4 /** \file util.cpp
5  *  \brief basic utility functions: maths, parsing expressions
6  */
7 
8 #include "hyper.h"
9 namespace hr {
10 
11 #if CAP_TIMEOFDAY
12 #if !CAP_SDL
13 int lastusec;
14 int uticks;
15 
SDL_GetTicks()16 EX int SDL_GetTicks() {
17   struct timeval tim;
18   gettimeofday(&tim, NULL);
19   int newusec = tim.tv_usec;
20   uticks += newusec - lastusec;
21   if(newusec <= lastusec)
22     uticks += 1000000;
23   lastusec = newusec;
24   return uticks / 1000;
25   }
26 
27 #endif
28 #endif
29 
sqr(long double x)30 EX long double sqr(long double x) { return x*x; }
31 
round_nearest(ld x)32 EX ld round_nearest(ld x) { if(x > 0) return int(x+.5); else return -int(.5-x); }
round_nearest(ld x,ld multiple_of)33 EX ld round_nearest(ld x, ld multiple_of) { return multiple_of * round_nearest(x / multiple_of); }
34 
gcd(int i,int j)35 EX int gcd(int i, int j) {
36   return i ? gcd(j%i, i) : j;
37   }
38 
gmod(int i,int j)39 EX int gmod(int i, int j) {
40   i %= j; if(i<0) i += j;
41   return i;
42   }
43 
zgmod(int a,int b)44 EX int zgmod(int a, int b) { return b ? gmod(a, b) : a; }
45 
szgmod(int a,int b)46 EX int szgmod(int a, int b) {
47   if(!b) return a;
48   a = gmod(a, b);
49   if(2*a >= b) return a - b;
50   return a;
51   }
52 
gdiv(int i,int j)53 EX int gdiv(int i, int j) {
54   return (i - gmod(i, j)) / j;
55   }
56 
frac(ld x)57 EX ld frac(ld x) {
58   x -= int(x);
59   if(x < 0) x++;
60   return x;
61   }
62 
lerp(ld a0,ld a1,ld x)63 EX ld lerp(ld a0, ld a1, ld x) {
64   return a0 + (a1-a0) * x;
65   }
66 
lerp(cld a0,cld a1,ld x)67 EX cld lerp(cld a0, cld a1, ld x) {
68   return a0 + (a1-a0) * x;
69   }
70 
ilerp(ld a0,ld a1,ld x)71 EX ld ilerp(ld a0, ld a1, ld x) {
72   return (x-a0) / (a1-a0);
73   }
74 
75 EX purehookset hooks_tests;
76 
simplify(const string & s)77 EX string simplify(const string& s) {
78   string res;
79   for(char c: s) if(isalnum(c)) res += c;
80   return res;
81   }
82 
appears(const string & haystack,const string & needle)83 EX bool appears(const string& haystack, const string& needle) {
84   return simplify(haystack).find(simplify(needle)) != string::npos;
85   }
86 
87 #if HDR
88 struct hr_parse_exception : hr_exception {
89   string s;
hr_parse_exceptionhr::hr_parse_exception90   hr_parse_exception(const string& z) : s(z) {}
~hr_parse_exceptionhr::hr_parse_exception91   ~hr_parse_exception() noexcept(true) {}
92   };
93 
94 struct exp_parser {
95   string s;
96   int at;
97   int line_number, last_line;
exp_parserhr::exp_parser98   exp_parser() { at = 0; line_number = 1; last_line = 0; }
99 
wherehr::exp_parser100   string where() {
101     if(s.find('\n')) return "(line " + its(line_number) + ", pos " + its(at-last_line) + ")";
102     else return "(pos " + its(at) + ")";
103     }
104 
105   map<string, cld> extra_params;
106 
okhr::exp_parser107   bool ok() { return at == isize(s); }
nexthr::exp_parser108   char next(int step=0) { if(at >= isize(s)-step) return 0; else return s[at+step]; }
109 
eathr::exp_parser110   bool eat(const char *c) {
111     int orig_at = at;
112     while(*c && *c == next()) at++, c++;
113     if(*c == 0) return true;
114     else at = orig_at;
115     return false;
116     }
117 
118   void skip_white();
119 
120   string next_token();
121 
snexthr::exp_parser122   char snext(int step=0) { skip_white(); return next(step); }
123 
124   cld parse(int prio = 0);
125 
rparsehr::exp_parser126   ld rparse(int prio = 0) { return validate_real(parse(prio)); }
iparsehr::exp_parser127   int iparse(int prio = 0) { return int(floor(rparse(prio) + .5)); }
128 
parseparhr::exp_parser129   cld parsepar() {
130     cld res = parse();
131     force_eat(")");
132     return res;
133     }
134 
validate_realhr::exp_parser135   ld validate_real(cld x) {
136     if(kz(imag(x))) throw hr_parse_exception("expected real number but " + lalign(-1, x) + " found at " + where());
137     return real(x);
138     }
139 
force_eathr::exp_parser140   void force_eat(const char *c) {
141     skip_white();
142     if(!eat(c)) throw hr_parse_exception("expected: " + string(c) + " at " + where());
143     }
144 
145   };
146 #endif
147 
skip_white()148 void exp_parser::skip_white() {
149   while(next() == ' ' || next() == '\n' || next() == '\r' || next() == '\t') {
150     if(next() == '\r') last_line++;
151     if(next() == '\n') {
152       line_number++, last_line = at;
153       }
154     at++;
155     }
156   }
157 
next_token()158 string exp_parser::next_token() {
159   skip_white();
160   string token;
161   while(true) {
162     char c = next();
163     if((c >= '0' && c <= '9') || (c == '.' && next(1) != '.') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')
164       token += c, at++;
165     else break;
166     }
167   return token;
168   }
169 
parse(int prio)170 cld exp_parser::parse(int prio) {
171   cld res;
172   skip_white();
173   if(eat("sin(")) res = sin(parsepar());
174   else if(eat("cos(")) res = cos(parsepar());
175   else if(eat("sinh(")) res = sinh(parsepar());
176   else if(eat("cosh(")) res = cosh(parsepar());
177   else if(eat("asin(")) res = asin(parsepar());
178   else if(eat("acos(")) res = acos(parsepar());
179   else if(eat("asinh(")) res = asinh(parsepar());
180   else if(eat("acosh(")) res = acosh(parsepar());
181   else if(eat("exp(")) res = exp(parsepar());
182   else if(eat("sqrt(")) res = sqrt(parsepar());
183   else if(eat("log(")) res = log(parsepar());
184   else if(eat("tan(")) res = tan(parsepar());
185   else if(eat("tanh(")) res = tanh(parsepar());
186   else if(eat("atan(")) res = atan(parsepar());
187   else if(eat("atanh(")) res = atanh(parsepar());
188   else if(eat("abs(")) res = abs(parsepar());
189   else if(eat("re(")) res = real(parsepar());
190   else if(eat("im(")) res = imag(parsepar());
191   else if(eat("conj(")) res = std::conj(parsepar());
192   else if(eat("floor(")) res = floor(validate_real(parsepar()));
193   else if(eat("frac(")) { res = parsepar(); res = res - floor(validate_real(res)); }
194   else if(eat("to01(")) { res = parsepar(); return atan(res) / ld(M_PI) + ld(0.5); }
195   else if(eat("edge(")) {
196     ld a = rparse(0);
197     force_eat(",");
198     ld b = rparse(0);
199     force_eat(")");
200     res = edge_of_triangle_with_angles(2*M_PI/a, M_PI/b, M_PI/b);
201     }
202   else if(eat("edge_angles(")) {
203     cld a = rparse(0);
204     force_eat(",");
205     cld b = rparse(0);
206     force_eat(",");
207     cld c = rparse(0);
208     force_eat(")");
209 
210     if (auto *angleunit = hr::at_or_null(extra_params, "angleunit")) {
211       a *= *angleunit;
212       b *= *angleunit;
213       c *= *angleunit;
214       }
215 
216     return edge_of_triangle_with_angles(real(a), real(b), real(c));
217     }
218   else if(eat("regradius(")) {
219     ld a = rparse(0);
220     force_eat(",");
221     ld b = rparse(0);
222     force_eat(")");
223     res = edge_of_triangle_with_angles(M_PI/2, M_PI/a, M_PI/b);
224     }
225   #if CAP_ARCM
226   else if(eat("arcmedge(")) {
227     vector<int> vals;
228     vals.push_back(iparse(0));
229     while(true) {
230       skip_white();
231       if(eat(",")) vals.push_back(iparse(0));
232       else break;
233       }
234     force_eat(")");
235     arcm::archimedean_tiling test;
236     test.faces = vals;
237     test.compute_sum();
238     test.compute_geometry();
239     res = test.edgelength;
240     if (auto *distunit = hr::at_or_null(extra_params, "distunit"))
241       res /= *distunit;
242     }
243   #endif
244   else if(eat("regangle(")) {
245     cld edgelen = parse(0);
246     if (auto *distunit = hr::at_or_null(extra_params, "distunit")) {
247       edgelen *= *distunit;
248       }
249 
250     force_eat(",");
251     ld edges = rparse(0);
252     force_eat(")");
253     ld alpha = M_PI / edges;
254     ld c = asin_auto(sin_auto(validate_real(edgelen)/2) / sin(alpha));
255     hyperpoint h = xpush(c) * spin(M_PI - 2*alpha) * xpush0(c);
256     ld result = 2 * atan2(h);
257     if(result < 0) result = -result;
258     while(result > 2 * M_PI) result -= 2 * M_PI;
259     if(result > M_PI) result = 2 * M_PI - result;
260 
261     if(arb::legacy) {
262       res = M_PI - result;
263       if (auto *angleofs = hr::at_or_null(extra_params, "angleofs"))
264         res -= *angleofs;
265       }
266     else
267       res = result;
268 
269     if (auto *angleunit = hr::at_or_null(extra_params, "angleunit"))
270       res /= *angleunit;
271     }
272   else if(eat("test(")) {
273     res = parsepar();
274     println(hlog, "res = ", res, ": ", fts(real(res), 10), ",", fts(imag(res), 10));
275     }
276   else if(eat("ifp(")) {
277     cld cond = parse(0);
278     force_eat(",");
279     cld yes = parse(0);
280     force_eat(",");
281     cld no = parsepar();
282     res = real(cond) > 0 ? yes : no;
283     }
284   else if(eat("wallif(")) {
285     cld val0 = parse(0);
286     force_eat(",");
287     cld val1 = parsepar();
288     if(real(extra_params["p"]) >= 3.5) res = val0;
289     else res = val1;
290     }
291   else if(eat("rgb(")) {
292     cld val0 = parse(0);
293     force_eat(",");
294     cld val1 = parse(0);
295     force_eat(",");
296     cld val2 = parsepar();
297     switch(int(real(extra_params["p"]) + .5)) {
298       case 1: res = val0; break;
299       case 2: res = val1; break;
300       case 3: res = val2; break;
301       default: res = 0;
302       }
303     }
304   else if(eat("let(")) {
305     string name = next_token();
306     force_eat("=");
307     cld val = parse(0);
308     force_eat(",");
309     dynamicval<cld> d(extra_params[name], val);
310     res = parsepar();
311     }
312   #if CAP_TEXTURE
313   else if(eat("txp(")) {
314     cld val = parsepar();
315     res = texture::get_txp(real(val), imag(val), int(real(extra_params["p"]) + .5)-1);
316     }
317   #endif
318   else if(next() == '(') at++, res = parsepar();
319   else {
320     string number = next_token();
321     if (auto *p = hr::at_or_null(extra_params, number)) res = *p;
322     else if (auto *p = hr::at_or_null(params, number)) res = (*p)->get_cld();
323     else if(number == "e") res = exp(1);
324     else if(number == "i") res = cld(0, 1);
325     else if(number == "p" || number == "pi") res = M_PI;
326     else if(number == "" && next() == '-') { at++; res = -parse(prio); }
327     else if(number == "") throw hr_parse_exception("number missing, " + where());
328     else if(number == "s") res = ticks / 1000.;
329     else if(number == "ms") res = ticks;
330     else if(number[0] == '0' && number[1] == 'x') res = strtoll(number.c_str()+2, NULL, 16);
331     else if(number == "mousex") res = mousex;
332     else if(number == "deg") res = degree;
333     else if(number == "ultra_mirror_dist") res = cgi.ultra_mirror_dist;
334     else if(number == "psl_steps") res = cgi.psl_steps;
335     else if(number == "single_step") res = cgi.single_step;
336     else if(number == "step") res = hdist0(tC0(currentmap->adj(cwt.at, 0)));
337     else if(number == "edgelen") res = hdist(get_corner_position(cwt.at, 0), get_corner_position(cwt.at, 1));
338     else if(number == "mousey") res = mousey;
339     else if(number == "random") res = randd();
340     else if(number == "mousez") res = cld(mousex - current_display->xcenter, mousey - current_display->ycenter) / cld(current_display->radius, 0);
341     else if(number == "shot") res = inHighQual ? 1 : 0;
342     else if(number[0] >= 'a' && number[0] <= 'z') throw hr_parse_exception("unknown value: " + number);
343     else if(number[0] >= 'A' && number[0] <= 'Z') throw hr_parse_exception("unknown value: " + number);
344     else if(number[0] == '_') throw hr_parse_exception("unknown value: " + number);
345     else { std::stringstream ss; res = 0; ss << number; ss >> res; }
346     }
347   while(true) {
348     skip_white();
349     #if CAP_ANIMATIONS
350     if(next() == '.' && next(1) == '.' && prio == 0) {
351       static const cld NO_DERIVATIVE(3.1, 2.5);
352       vector<array<cld, 4>> rest = { make_array(res, NO_DERIVATIVE, res, NO_DERIVATIVE) };
353       bool second = true;
354       while(next() == '.' && next(1) == '.') {
355         /* spline interpolation */
356         if(next(2) == '/') {
357           at += 3;
358           rest.back()[second ? 3 : 1] = parse(10);
359           continue;
360           }
361         /* sharp end */
362         else if(next(2) == '|') {
363           at += 3;
364           rest.back()[2] = parse(10);
365           rest.back()[3] = NO_DERIVATIVE;
366           second = true;
367           continue;
368           }
369         at += 2;
370         auto val = parse(10);
371         rest.emplace_back(make_array(val, NO_DERIVATIVE, val, NO_DERIVATIVE));
372         second = false;
373         }
374       ld v = ticks * (isize(rest)-1.) / anims::period;
375       int vf = v;
376       v -= vf;
377       if(isize(rest) == 1) rest.push_back(rest[0]);
378       vf %= (isize(rest)-1);
379       auto& lft = rest[vf];
380       auto& rgt = rest[vf+1];
381       if(lft[3] == NO_DERIVATIVE && rgt[1] == NO_DERIVATIVE)
382         res = lerp(lft[2], rgt[0], v);
383       else if(rgt[1] == NO_DERIVATIVE)
384         res = lerp(lft[2] + lft[3] * v, rgt[0], v*v);
385       else if(lft[3] == NO_DERIVATIVE)
386         res = lerp(lft[2], rgt[0] + rgt[1] * (v-1), (2-v)*v);
387       else {
388         res = lerp(lft[2] + lft[3] * v, rgt[0] + rgt[1] * (v-1), v*v*(3-2*v));
389         }
390       return res;
391       }
392     else
393     #endif
394     if(next() == '+' && prio <= 10) at++, res = res + parse(20);
395     else if(next() == '-' && prio <= 10) at++, res = res - parse(20);
396     else if(next() == '*' && prio <= 20) at++, res = res * parse(30);
397     else if(next() == '/' && prio <= 20) at++, res = res / parse(30);
398     else if(next() == '^') at++, res = pow(res, parse(40));
399     else break;
400     }
401   return res;
402   }
403 
parseld(const string & s)404 EX ld parseld(const string& s) {
405   exp_parser ep;
406   ep.s = s;
407   return ep.rparse();
408   }
409 
parseint(const string & s)410 EX int parseint(const string& s) {
411   exp_parser ep;
412   ep.s = s;
413   return ep.iparse();
414   }
415 
available_functions()416 EX string available_functions() {
417   return
418     "(a)sin(h), (a)cos(h), (a)tan(h), exp, log, abs, re, im, conj, let(t=...,...t...), floor, frac, sqrt, to01, random, edge(7,3), regradius(7,3), ifp(a,v,w) [if positive]";
419   }
420 
available_constants()421 EX string available_constants() {
422   return
423     "e, i, pi, s, ms, mousex, mousey, mousez, shot [1 if taking screenshot/animation]";
424   }
425 
426 #if HDR
427 struct bignum {
428   static const int BASE = 1000000000;
429   static const long long BASE2 = BASE * (long long)BASE;
430   vector<int> digits;
bignumhr::bignum431   bignum() {}
bignumhr::bignum432   bignum(int i) : digits() { digits.push_back(i); }
behr::bignum433   void be(int i) { digits.resize(1); digits[0] = i; }
434   bignum& operator +=(const bignum& b);
435   void addmul(const bignum& b, int factor);
436   string get_str(int max_length) const;
437   bignum(ld d);
438 
439   bool operator < (const bignum&) const;
operator >hr::bignum440   bool operator > (const bignum& b) const { return b < self; }
441 
leadinghr::bignum442   ld leading() const {
443     switch(isize(digits)) {
444       case 0:
445         return 0;
446       case 1:
447         return digits.back();
448       default:
449         return digits.back() + ld(digits[isize(digits)-2]) / BASE;
450       }
451     }
452 
approxhr::bignum453   ld approx() const {
454     return leading() * pow(BASE, isize(digits) - 1);
455     }
456 
log_approxhr::bignum457   ld log_approx() const {
458     return log(leading()) * log(BASE) * (isize(digits) - 1);
459     }
460 
approx_divhr::bignum461   ld approx_div(const bignum& b) const {
462     return leading() / b.leading() * pow(BASE, isize(digits) - isize(b.digits));
463     }
464 
approx_inthr::bignum465   int approx_int() const {
466     if(isize(digits) > 1) return BASE;
467     if(digits.empty()) return 0;
468     return digits[0];
469     }
470 
nonzerohr::bignum471   bool nonzero() { return approx_ld() != 0; }
472 
473   bignum randomized_div(int x) const;
474 
approx_ldhr::bignum475   ld approx_ld() const {
476     ld res = 0;
477     for(int i=0; i<isize(digits); i++) res += digits[i] * pow(BASE, i);
478     return res;
479     }
480 
approx_llhr::bignum481   long long approx_ll() const {
482     if(isize(digits) > 2) return BASE2;
483     if(digits.empty()) return 0;
484     if(isize(digits) == 1) return digits[0];
485     return digits[0] + digits[1] * (long long) BASE;
486     }
487 
488   #if CAP_GMP
as_mpqhr::bignum489   mpq_class as_mpq() const {
490     string s = get_str(999999);
491     string t;
492     for(char c: s) if(c != ' ') t += c;
493     return mpq_class(t);
494     }
495   #endif
496 
operator +(bignum a,const bignum & b)497   friend inline bignum operator +(bignum a, const bignum& b) { a.addmul(b, 1); return a; }
operator -(bignum a,const bignum & b)498   friend inline bignum operator -(bignum a, const bignum& b) { a.addmul(b, -1); return a; }
499   };
500 #endif
501 
operator +=(const bignum & b)502 bignum& bignum::operator +=(const bignum& b) {
503   int K = isize(b.digits);
504   if(K > isize(digits)) digits.resize(K);
505   int carry = 0;
506   for(int i=0; i<K || carry; i++) {
507     if(i >= isize(digits)) digits.push_back(0);
508     digits[i] += carry;
509     if(i < K) digits[i] += b.digits[i];
510     if(digits[i] >= BASE) {
511       digits[i] -= BASE;
512       carry = 1;
513       }
514     else carry = 0;
515     }
516   return *this;
517   }
518 
operator <(const bignum & b) const519 bool bignum::operator < (const bignum& b) const {
520   if(isize(digits) != isize(b.digits))
521     return isize(digits) < isize(b.digits);
522   for(int i = isize(digits)-1; i>=0; i--)
523     if(digits[i] != b.digits[i])
524       return digits[i] < b.digits[i];
525   return false;
526   }
527 
randomized_div(int x) const528 bignum bignum::randomized_div(int x) const {
529   bignum res = self;
530   long long carry = 0;
531   int K = isize(res.digits);
532   for(int i=K-1; i>=0; i--) {
533     carry *= BASE;
534     carry += digits[i];
535     // strange compiler buug:
536     // if I do / and %, function 'divmod' is called, and it complains on launch that divmod is unimplemented
537     res.digits[i] = int(carry / x);
538     carry -= res.digits[i] * (long long)(x);
539     }
540   while(isize(res.digits) && res.digits.back() == 0) res.digits.pop_back();
541   if(rand() % x < carry) res += 1;
542   return res;
543   }
544 
addmul(const bignum & b,int factor)545 void bignum::addmul(const bignum& b, int factor) {
546   int K = isize(b.digits);
547   if(K > isize(digits)) digits.resize(K);
548   int carry = 0;
549   for(int i=0; i<K || (carry > 0 || carry < -1) || (carry == -1 && i < isize(digits)); i++) {
550     if(i >= isize(digits)) digits.push_back(0);
551     long long l = digits[i];
552     l += carry;
553     if(i < K) l += b.digits[i] * factor;
554     carry = 0;
555     if(l >= BASE) carry = int(l / BASE);
556     if(l < 0) carry = -int((BASE-1-l) / BASE);
557     l -= carry * BASE;
558     digits[i] = int(l);
559     }
560   if(carry < 0) digits.back() -= BASE;
561   while(isize(digits) && digits.back() == 0) digits.pop_back();
562   }
563 
hrand(bignum b)564 EX bignum hrand(bignum b) {
565   bignum res;
566   int d = isize(b.digits);
567   while(true) {
568     res.digits.resize(d);
569     for(int i=0; i<d-1; i++) res.digits[i] = hrand(bignum::BASE);
570     res.digits.back() = hrand(b.digits.back() + 1);
571     if(res < b) return res;
572     }
573   }
574 
operator ++(bignum & b,int)575 EX void operator ++(bignum &b, int) {
576   int i = 0;
577   while(true) {
578     if(isize(b.digits) == i) { b.digits.push_back(1); break; }
579     else if(b.digits[i] == bignum::BASE-1) {
580       b.digits[i] = 0;
581       i++;
582       }
583     else {
584       b.digits[i]++;
585       break;
586       }
587     }
588   }
589 
operator --(bignum & b,int)590 EX void operator --(bignum &b, int) {
591   int i = 0;
592   while(true) {
593     if(isize(b.digits) == i) { b.digits.push_back(bignum::BASE-1); break; }
594     else if(b.digits[i] == 0) {
595       b.digits[i] = bignum::BASE-1;
596       i++;
597       }
598     else {
599       b.digits[i]--;
600       break;
601       }
602     }
603   }
604 
get_str(int max_length) const605 string bignum::get_str(int max_length) const {
606   if(digits.empty()) return "0";
607   string ret = its(digits.back());
608   for(int i=isize(digits)-2; i>=0; i--) {
609     if(isize(ret) > max_length && i) {
610       ret += XLAT(" (%1 more digits)", its(9 * (i+1)));
611       return ret;
612       }
613 
614     ret += " ";
615     string val = its(digits[i]);
616     while(isize(val) < 9) val = "0" + val;
617     ret += val;
618     }
619   return ret;
620   }
621 
short_form(bignum b)622 EX string short_form(bignum b) {
623   if(b < 0) return "-" + short_form(0-b);
624   else if(b < 100000) return its(b.approx_int());
625   else {
626     long long val;
627     int q;
628     if(isize(b.digits) >= 2) {
629       q = max(isize(b.digits) - 2, 0);
630       val = b.digits[q] + (long long)(bignum::BASE) * b.digits[q+1];
631       }
632     else {
633       q = 0;
634       val = b.digits[0];
635       }
636 
637     int digits = q * 9;
638     while(val >= 1000) { val /= 10; digits++; }
639     string str = its(int(val)) + "E" + its(digits + 2);
640     str.insert(1, ".");
641     return str;
642     }
643   }
644 
bignum(ld d)645 bignum::bignum(ld d) {
646   if(d == 0) return;
647   int n = 1;
648   while(d > BASE) d /= BASE, n++;
649   digits.resize(n);
650   n--;
651   while(n >= 0) { digits[n] = int(d); d -= digits[n]; d *= BASE; n--; }
652   }
653 
654 /** in s, replace occurences of a with b */
replace_str(string & s,string a,string b)655 EX void replace_str(string& s, string a, string b) {
656   while(s.find(a) != string::npos)
657     s.replace(s.find(a), isize(a), b);
658   }
659 
660 #if CAP_ZLIB
661 /* compression/decompression */
662 
compress_string(string s)663 EX string compress_string(string s) {
664   z_stream strm;
665   strm.zalloc = Z_NULL;
666   strm.zfree = Z_NULL;
667   strm.opaque = Z_NULL;
668   println(hlog, "pre init");
669   auto ret = deflateInit(&strm, 9);
670   if(ret != Z_OK) throw hr_exception("z-error");
671   println(hlog, "init ok");
672   strm.avail_in = isize(s);
673   strm.next_in = (Bytef*) &s[0];
674   vector<char> buf(10000000, 0);
675   strm.avail_out = 10000000;
676   strm.next_out = (Bytef*) &buf[0];
677   if(deflate(&strm, Z_FINISH) != Z_STREAM_END) throw hr_exception("z-error-2");
678   println(hlog, "deflate ok");
679   string out(&buf[0], (char*)(strm.next_out) - &buf[0]);
680   println(hlog, isize(s), " -> ", isize(out));
681   return out;
682   }
683 
decompress_string(string s)684 EX string decompress_string(string s) {
685   z_stream strm;
686   strm.zalloc = Z_NULL;
687   strm.zfree = Z_NULL;
688   strm.opaque = Z_NULL;
689   auto ret = inflateInit(&strm);
690   if(ret != Z_OK) throw hr_exception("z-error");
691   strm.avail_in = isize(s);
692   strm.next_in = (Bytef*) &s[0];
693   vector<char> buf(10000000, 0);
694   strm.avail_out = 10000000;
695   strm.next_out = (Bytef*) &buf[0];
696   if(inflate(&strm, Z_FINISH) != Z_STREAM_END) throw hr_exception("z-error-2");
697   string out(&buf[0], (char*)(strm.next_out) - &buf[0]);
698   println(hlog, isize(s), " -> ", isize(out));
699   return out;
700   }
701 #endif
702 
file_exists(string fname)703 EX bool file_exists(string fname) {
704   return access(fname.c_str(), F_OK) != -1;
705   }
706 
open_url(string s)707 EX void open_url(string s) {
708   #if ISWEB
709   EM_ASM_({
710     window.open(UTF8ToString($0, 1000));
711     }, s.c_str());
712   #else
713 
714 #ifdef WINDOWS
715   ShellExecute(0, 0, s.c_str(), 0, 0, SW_SHOW);
716 #endif
717 
718 #ifdef LINUX
719   ignore(system(("xdg-open "+s).c_str()));
720 #endif
721 
722 #ifdef MAC
723   ignore(system(("open "+s).c_str()));
724 #endif
725 #endif
726   }
727 
728 const char *urlhex = "0123456789ABCDEF";
open_wiki(const char * title)729 EX void open_wiki(const char *title) {
730   string url = "https://hyperrogue.miraheze.org/wiki/";
731   unsigned char c;
732   for (size_t i = 0; (c = title[i]); ++i) {
733     if (('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_' || c == '-')
734       url += c;
735     else if (c == ' ')
736       url += "_";
737     else
738       url += string("%") + urlhex[c/16] + urlhex[c%16];
739     }
740   open_url(url);
741 }
742 
floyd_warshall(vector<vector<char>> & v)743 EX void floyd_warshall(vector<vector<char>>& v) {
744   int N = isize(v);
745   for(int k=0; k<N; k++)
746   for(int i=0; i<N; i++)
747   for(int j=0; j<N; j++)
748     v[i][j] = min<int>(v[i][j], v[i][k] + v[k][j]);
749   }
750 }
751