text: #include "format.h"
  #include "output.h"
  
  #include <util/string/cast.h>
  
  #include <cmath>
  
  namespace NFormatPrivate {
      static inline i64 Round(double value) {
          double res1 = floor(value);
          double res2 = ceil(value);
          return (value - res1 < res2 - value) ? (i64)res1 : (i64)res2;
      }
  
      static inline IOutputStream& PrintDoubleShortly(IOutputStream& os, const double& d) {
          os << Prec(d, 3);
          return os;
      }
  } //NFormatPrivate
  
  template <>
  void Out<NFormatPrivate::THumanReadableSize>(IOutputStream& stream, const NFormatPrivate::THumanReadableSize& value) {
      ui64 base = value.Format == SF_BYTES ? 1024 : 1000;
      ui64 base2 = base * base;
      ui64 base3 = base * base2;
      ui64 base4 = base * base3;
  
      double v = value.Value;
      if (v < 0) {
          stream << "-";
          v = -v;
      }
  
      if (v < base) {
          NFormatPrivate::PrintDoubleShortly(stream, v);
      } else if (value.Value < base2) {
          NFormatPrivate::PrintDoubleShortly(stream, v / (double)base) << 'K';
      } else if (value.Value < base3) {
          NFormatPrivate::PrintDoubleShortly(stream, v / (double)base2) << 'M';
      } else if (value.Value < base4) {
          NFormatPrivate::PrintDoubleShortly(stream, v / (double)base3) << 'G';
      } else {
          NFormatPrivate::PrintDoubleShortly(stream, v / (double)base4) << 'T';
      }
  
      if (value.Format == SF_BYTES) {
          if (v < base) {
              stream << "B";
          } else {
              stream << "iB";
          }
      }
  }
  
  template <>
  void Out<NFormatPrivate::THumanReadableDuration>(IOutputStream& os, const NFormatPrivate::THumanReadableDuration& hr) {
      TTempBuf buf;
      TMemoryOutput ss(buf.Data(), buf.Size());
  
      do {
          ui64 microSeconds = hr.Value.MicroSeconds();
          if (microSeconds < 1000) {
              ss << microSeconds << "us";
              break;
          }
          if (microSeconds < 1000 * 1000) {
              NFormatPrivate::PrintDoubleShortly(ss, (double)microSeconds / 1000.0) << "ms";
              break;
          }
  
          double seconds = (double)(hr.Value.MilliSeconds()) / 1000.0;
          if (seconds < 60) {
              NFormatPrivate::PrintDoubleShortly(ss, seconds) << 's';
              break;
          }
  
          ui64 s = NFormatPrivate::Round(seconds * 1000 + 0.5) / 1000;
  
          ui64 m = s / 60;
          s = s % 60;
  
          ui64 h = m / 60;
          m = m % 60;
  
          ui64 d = h / 24;
          h = h % 24;
  
          ui64 times[] = {d, h, m, s};
          char names[] = {'d', 'h', 'm', 's'};
          bool first = true;
  
          for (size_t i = 0; i < Y_ARRAY_SIZE(times); ++i) {
              if (times[i] > 0) {
                  if (!first)
                      ss << ' ';
                  ss << times[i] << names[i];
                  first = false;
              }
          }
      } while (false);
  
      size_t written = buf.Size() - ss.Avail();
      os.Write(buf.Data(), written);
  }
  
  void Time(IOutputStream& l) {
      l << millisec();
  }
  
  void TimeHumanReadable(IOutputStream& l) {
      char timeStr[30];
      const time_t t = time(nullptr);
  
      l << ctime_r(&t, timeStr);
  }
  