// Hyperbolic Rogue -- HyperRogue streams // Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details /** \file hprint.cpp * \brief Routines related to displaying various things, and writing them to console and files. Nicer than the standard C++ streams. */ #include "hyper.h" namespace hr { EX FILE *debugfile; #if HDR #define DF_INIT 1 // always display these #define DF_MSG 2 // always display these #define DF_WARN 4 // always display these #define DF_ERROR 8 // always display these #define DF_STEAM 16 #define DF_GRAPH 32 #define DF_TURN 64 #define DF_FIELD 128 #define DF_GEOM 256 #define DF_MEMORY 512 #define DF_TIME 1024 // a flag to display timestamps #define DF_GP 2048 #define DF_POLY 4096 #define DF_LOG 8192 #define DF_VERTEX 16384 #define DF_KEYS "imwesxufgbtoplv" #endif EX int debugflags = DF_INIT | DF_ERROR | DF_WARN | DF_MSG | DF_TIME | DF_LOG; EX string s0; EX string its(int i) { char buf[64]; sprintf(buf, "%d", i); return buf; } EX string itsh8(int i) {static char buf[16]; sprintf(buf, "%08X", i); return buf; } EX string fts(ld x, int prec IS(6)) { std::stringstream ss; ss.precision(prec); ss << x; return ss.str(); } map pointer_indices; EX string index_pointer(void *v) { if(v == nullptr) return "0"; if(!pointer_indices.count(v)) pointer_indices[v] = isize(pointer_indices); string res; int i = pointer_indices[v] + 1; while(i) { res += ('A' + (i % 26)); i /= 26; } return res; } #if HDR inline string ONOFF(bool b) { return XLAT(b ? "ON" : "OFF"); } struct hstream { virtual void write_char(char c) = 0; virtual void write_chars(const char* c, size_t q) { while(q--) write_char(*(c++)); } virtual char read_char() = 0; virtual void read_chars(char* c, size_t q) { while(q--) *(c++) = read_char(); } virtual color_t get_vernum() { return VERNUM_HEX; } template void write(const T& t) { hwrite(*this, t); } template void read(T& t) { hread(*this, t); } template T get() { T t; hread(*this, t); return t; } template T get_raw() { T t; hread_raw(*this, t); return t; } }; template void hwrite_raw(hstream& hs, const T& c) { hs.write_chars((char*) &c, sizeof(T)); } template void hread_raw(hstream& hs, T& c) { hs.read_chars((char*) &c, sizeof(T)); } template::value || std::is_enum::value>::type> void hwrite(hstream& hs, const T& c) { hwrite_raw(hs, c); } template::value || std::is_enum::value>::type> void hread(hstream& hs, T& c) { hread_raw(hs, c); } inline void hwrite(hstream& hs, const string& s) { if(isize(s) >= 255) { hs.write_char((char)255); hs.write(isize(s)); } for(char c: s) hs.write_char(c); } inline void hread(hstream& hs, string& s) { s = ""; int l = (unsigned char) hs.read_char(); if(l == 255) l = hs.get(); for(int i=0; i void hwrite(hstream& hs, const array& a) { for(auto &ae: a) hwrite(hs, ae); } template void hread(hstream& hs, array& a) { for(auto &ae: a) hread(hs, ae); } inline void hread(hstream& hs, hyperpoint& h) { for(int i=0; i void hwrite(hstream& hs, const vector& a) { hwrite(hs, isize(a)); for(auto &ae: a) hwrite(hs, ae); } template void hread(hstream& hs, vector& a) { a.resize(hs.get()); for(auto &ae: a) hread(hs, ae); } template void hwrite(hstream& hs, const map& a) { hwrite(hs, isize(a)); for(auto &ae: a) hwrite(hs, ae.first, ae.second); } template void hread(hstream& hs, map& a) { a.clear(); int N = hs.get(); for(int i=0; i void hwrite(hstream& hs, const C& c, const C1& c1, const CS&... cs) { hwrite(hs, c); hwrite(hs, c1, cs...); } template void hread(hstream& hs, C& c, C1& c1, CS&... cs) { hread(hs, c); hread(hs, c1, cs...); } struct hstream_exception : hr_exception { hstream_exception() {} }; struct fhstream : hstream { color_t vernum; virtual color_t get_vernum() override { return vernum; } FILE *f; virtual void write_char(char c) override { write_chars(&c, 1); } virtual void write_chars(const char* c, size_t i) override { if(fwrite(c, i, 1, f) != 1) throw hstream_exception(); } virtual void read_chars(char* c, size_t i) override { if(fread(c, i, 1, f) != 1) throw hstream_exception(); } virtual char read_char() override { char c; read_chars(&c, 1); return c; } fhstream() { f = NULL; vernum = VERNUM_HEX; } fhstream(const string pathname, const char *mode) { f = fopen(pathname.c_str(), mode); vernum = VERNUM_HEX; } ~fhstream() { if(f) fclose(f); } }; struct shstream : hstream { string s; int pos; shstream(const string& t = "") : s(t) { pos = 0; } virtual void write_char(char c) { s += c; } virtual char read_char() { if(pos == isize(s)) throw hstream_exception(); return s[pos++]; } }; inline void print(hstream& hs) {} template string sprint(const CS&... cs) { shstream hs; print(hs, cs...); return hs.s; } template void print(hstream& hs, const C& c, const C1& c1, const CS&... cs) { print(hs, c); print(hs, c1, cs...); } template void println(hstream& hs, const CS&... cs) { print(hs, cs...); hs.write_char('\n'); } inline string spaced(int i) { return its(i); } inline string spaced(color_t col) { return itsh8(col); } inline string spaced(const string& s) { return s; } inline string spaced(ld x) { return fts(x, 10); } template string spaced_of(T a[], int q) { string s = spaced(a[0]); for(int i=1; i string spaced(const array& a) { return spaced_of(&a[0], isize(a)); } template string spaced(const C& c, const C1& c1, const CS&... cs) { return spaced(c) + " " + spaced(c1, cs...); } bool scan(fhstream& hs, int&); bool scan(fhstream& hs, ld&); bool scan(fhstream& hs, string&); bool scan(fhstream& hs, color_t& c); template bool scan(fhstream& hs, C& c, C1& c1, CS&... cs) { return scan(hs, c) && scan(hs, c1, cs...); } string scanline(fhstream& hs); template T scan(fhstream& hs) { T t {}; scan(hs, t); return t; } // copied from: https://stackoverflow.com/questions/16387354/template-tuple-calling-a-function-on-each-element namespace detail { template struct seq { }; template struct gen_seq : gen_seq { }; template struct gen_seq<0, Is...> : seq { }; template void for_each(T&& t, F f, seq) { auto l = { (f(std::get(t)), 0)... }; ignore(l); } } template void for_each_in_tuple(std::tuple const& t, F f) { detail::for_each(t, f, detail::gen_seq()); } inline void print(hstream& hs, const string& s) { hs.write_chars(s.c_str(), isize(s)); } inline void print(hstream& hs, int i) { print(hs, its(i)); } inline void print(hstream& hs, ld x) { print(hs, fts(x, 6)); } inline void print(hstream& hs, color_t col) { print(hs, itsh8(col)); } template void print(hstream& hs, const walker& w) { print(hs, "[", w.at, "/", w.spin, "/", w.mirrored, "]"); } struct comma_printer { bool first; hstream& hs; template void operator() (const T& t) { if(first) first = false; else print(hs, ","); print(hs, t); } comma_printer(hstream& hs) : first(true), hs(hs) {} }; template void print(hstream& hs, const array& a) { print(hs, "("); comma_printer c(hs); for(const T& t: a) c(t); print(hs, ")"); } template void print(hstream& hs, const vector& a) { print(hs, "("); comma_printer c(hs); for(const T& t: a) c(t); print(hs, ")"); } inline void print(hstream& hs, const hyperpoint h) { print(hs, (const array&)h); } inline void print(hstream& hs, const transmatrix T) { print(hs, "("); comma_printer c(hs); for(int i=0; i void print(hstream& hs, const pair & t) { print(hs, "(", t.first, ",", t.second, ")"); } template void print(hstream& hs, const tuple & t) { print(hs, "("); comma_printer p(hs); for_each_in_tuple(t, p); print(hs, ")"); } #ifndef SPECIAL_LOGGER inline void special_log(char c) { if(debugfile) fputc(c, debugfile); putchar(c); } #endif #if !CAP_SDL && CAP_TIMEOFDAY int SDL_GetTicks(); #endif struct logger : hstream { int indentation; bool doindent; logger() { doindent = false; } virtual void write_char(char c) { if(doindent) { doindent = false; if(debugflags & DF_TIME) { int t = SDL_GetTicks(); if(t < 0) t = 999999; t %= 1000000; string s = its(t); while(isize(s) < 6) s = "0" + s; for(char c: s) special_log(c); special_log(' '); } for(int i=0; i void println_log(T... t) { println(hlog, t...); } template void print_log(T... t) { print(hlog, t...); } #ifdef __GNUC__ __attribute__((__format__ (__printf__, 1, 2))) #endif inline string format(const char *fmt, ...) { char buf[1000]; va_list ap; va_start(ap, fmt); vsnprintf(buf, 1000, fmt, ap); va_end(ap); return buf; } inline void print(hstream& hs, heptagon* h) { print(hs, "H", index_pointer(h)); } inline void print(hstream& hs, cell* h) { print(hs, "C", index_pointer(h)); } inline void print(hstream& hs, hrmap* h) { print(hs, "M", index_pointer(h)); } inline void print(hstream& hs, cellwalker cw) { if(cw.at) print(hs, "[", cw.at, "/", cw.at->type, ":", cw.spin, ":", cw.mirrored, "]"); else print(hs, "[NULL]"); } struct indenter { dynamicval ind; indenter(int i = 2) : ind(hlog.indentation, hlog.indentation + (i)) {} }; struct indenter_finish : indenter { indenter_finish(bool b = true): indenter(b ? 2:0) {} ~indenter_finish() { if(hlog.indentation != ind.backup) println(hlog, "(done)"); } }; #endif EX void print(hstream& hs, cld x) { int parts = 0; if(kz(real(x))) { print(hs, real(x)); parts++; } if(kz(imag(x))) { if(parts && imag(x) > 0) print(hs, "+"); parts++; print(hs, imag(x), "i"); } if(!parts) print(hs, 0); } EX string fts_fixed(ld x, int prec IS(6)) { std::stringstream ss; ss.precision(prec); ss << std::fixed << x; return ss.str(); } bool scan(fhstream& hs, int& i) { return fscanf(hs.f, "%d", &i) == 1; } bool scan(fhstream& hs, color_t& c) { return fscanf(hs.f, "%x", &c) == 1; } bool scan(fhstream& hs, ld& x) { return fscanf(hs.f, "%lf", &x) == 1; } bool scan(fhstream& hs, string& s) { char t[10000]; t[0] = 0; int err = fscanf(hs.f, "%9500s", t); s = t; return err == 1 && t[0]; } string scanline(fhstream& hs) { char buf[10000]; buf[0] = 0; ignore(fgets(buf, 10000, hs.f)); return buf; } /* string fts_smartdisplay(ld x, int maxdisplay) { string rv; if(x > 1e9 || x < -1e9) retrun fts(x); if(x<0) { rv = "-"; x = -x; } int i = int(x); rv += its(i); x -= i; bool nonzero = i; if(x == 0) return rv; if(x < 1e-9 && nonzero) return rv; rv += "."; while(maxdisplay > 0) { x *= 10; rv += '0' + int(x); if(int(x)) nonzero = true; x -= int(x); if(x == 0) return rv; if(x < 1e-9 && nonzero) return rv; maxdisplay--; } } */ EX string cts(char c) { char buf[8]; buf[0] = c; buf[1] = 0; return buf; } EX string llts(long long i) { // sprintf does not work on Windows IIRC if(i < 0) return "-" + llts(-i); if(i < 10) return its((int) i); return llts(i/10) + its(i%10); } EX string itsh(unsigned int i) {static char buf[16]; sprintf(buf, "%03X", i); return buf; } EX string itsh(int i) {static char buf[16]; sprintf(buf, "%03X", i); return buf; } EX string itsh2(int i) {static char buf[16]; sprintf(buf, "%02X", i); return buf; } EX string itsh(unsigned long long i) { int i0 = int(i); int i1 = int(i >> 32); if(i1) return itsh(i1) + itsh8(i0); else return itsh(i0); } EX logger hlog; // kz: utility for printing // if it is close to 0, assume it is floating errors EX ld kz(ld x) { if(abs(x) < 1e-6) return 0; return x; } EX hyperpoint kz(hyperpoint h) { for(int d=0; d vector kz(vector v) { for(auto& el: v) el = kz(el); return v; } #endif EX string pick123() { return cts('1' + rand() % 3); } EX string pick12() { return cts('1' + rand() % 2); } #if HDR template string serialize(const T& data) { shstream shs; hwrite(shs, data); return shs.s; } template T deserialize(const string& s) { shstream shs; shs.s = s; T data; hread(shs, data); return data; } #endif EX string as_cstring(string o) { string s = "string(\""; for(char c: o) s += format("\\x%02x", (unsigned char) c); s += format("\", %d)", isize(o)); return s; } #if HDR #if ISANDROID #define DEBB(r,x) #define DEBB0(r,x) #define DEBBI(r,x) #else #define DEBB(r,x) { if(debugflags & (r)) { println_log x; } } #define DEBB0(r,x) { if(debugflags & (r)) { print_log x; } } #define DEBBI(r,x) { if(debugflags & (r)) { println_log x; } } indenter_finish _debbi(debugflags & (r)); #endif #endif #if HDR template string lalign(int len, T... t) { shstream hs; print(hs, t...); while(isize(hs.s) < len) hs.s += " "; return hs.s; } #endif }