<commit_before>#include <assert.h>

#include "global.h"

#include "Level.h"
#include "blocks.h"
#include "2d/cube.h"

void begin_compound(void *context, nbt::String name) {
  if (name.compare("Level") == 0) {
    ((Level*)context)->islevel = true;
  }
}

void register_int(void *context, nbt::String name, nbt::Int i) {
  if (name.compare("xPos") == 0) {
    ((Level *)context)->xPos = i;
    return;
  }

  if (name.compare("zPos") == 0) {
    ((Level *)context)->zPos = i;
    return;
  }
}

void register_byte_array(void *context, nbt::String name, nbt::ByteArray *byte_array) {
  if (name.compare("Blocks") == 0) {
    ((Level *)context)->blocks = byte_array;
    return;
  }

  if (name.compare("SkyLight") == 0) {
    ((Level *)context)->skylight = byte_array;
    return;
  }

  if (name.compare("HeightMap") == 0) {
    ((Level *)context)->heightmap = byte_array;
    return;
  }

  if (name.compare("BlockLight") == 0) {
    ((Level *)context)->blocklight = byte_array;
    return;
  }
}

void error_handler(void *context, size_t where, const char *why) {
  ((Level *)context)->grammar_error = true;
  ((Level *)context)->grammar_error_where = where;
  ((Level *)context)->grammar_error_why = why;
}

Level::~Level(){
  if (!ignore_blocks && islevel) {
    delete blocks;
    delete skylight;
    delete heightmap;
    delete blocklight;
  }
}

Level::Level(const char *path, bool ignore_blocks)
  : path(path), ignore_blocks(ignore_blocks)
{
  xPos = 0;
  zPos = 0;
  islevel = false;
  grammar_error = false;

  nbt::Parser parser(this);

  if (!ignore_blocks) {
    parser.register_byte_array = register_byte_array;
  }

  parser.register_int = register_int;
  parser.begin_compound = begin_compound;
  parser.error_handler = error_handler;

  try {
    parser.parse_file(path);
  } catch(nbt::bad_grammar &bg) {
    grammar_error = true;
  }
}

/**
 * Blocks[ z + ( y * ChunkSizeY(=128) + ( x * ChunkSizeY(=128) * ChunkSizeZ(=16) ) ) ];
 */
nbt::Byte bget(nbt::ByteArray *blocks, int x, int z, int y) {
  assert(x >= 0 && x < mc::MapX);
  assert(z >= 0 && z < mc::MapZ);
  assert(y >= 0 && y < mc::MapY);
  int p = y + (z * mc::MapY + (x * mc::MapY * mc::MapZ));
  assert (p >= 0 && p < blocks->length);
  return blocks->values[p];
}

nbt::Byte bsget(nbt::ByteArray *skylight, int x, int z, int y) {
  assert(x >= 0 && x < mc::MapX);
  assert(z >= 0 && z < mc::MapZ);
  assert(y >= 0 && y < mc::MapY);
  int p = y + (z * mc::MapY + (x * mc::MapY * mc::MapZ));
  int ap = p / 2;

  assert (ap >= 0 && ap < skylight->length);

  // force unsigned
  uint8_t bp = skylight->values[ap] & 0xff;

  if (p % 2 == 0) {
    return bp >> 4;
  }
  else {
    return bp & 0xf;
  }
}

void transform_xz(settings_t *s, int &x, int &z) {
  /*if (s->flip) {
    int t = x;
    x = z;
    z = mc::MapZ - t - 1;
  }

  if (s->invert) {
    z = mc::MapZ - z - 1;
    x = mc::MapX - x - 1;
  }*/
}

inline void apply_shading(settings_t *s, int bl, int sl, int hm, int y, Color &c) {
  // if night, darken all colors not emitting light
  if (s->night) {
    c.darken((0xd0 * (16 - bl)) / 16);
  }

  c.darken((mc::MapY - y));

  c.darken(sl);
}

inline bool cavemode_isopen(int bt) {
  switch(bt) {
    case mc::Air: return true;
    case mc::Leaves: return true;
    default: return false;
  }
}

inline bool cavemode_ignore_block(settings_t *s, int x, int z, int y, int bt, nbt::ByteArray *blocks, bool &cave_initial) {
  if (cave_initial) {
    if (!cavemode_isopen(bt)) {
      cave_initial = false;
      return true;
    }

    return true;
  }

  if (!cavemode_isopen(bt)) {
    if (y < s->top && cavemode_isopen(bget(blocks, x, z, y + 1))) {
      return false;
    }
  }

  return true;
}

class BlockRotation {
private:
  settings_t *s;
  nbt::ByteArray *byte_array;

  void transform_xzy(int& x, int& z, int& y) {
    switch (s->rotation) {
      case 270:
        z = mc::MapZ - z - 1;
        x = mc::MapX - x - 1;
        {
          int t = x;
          x = z;
          z = mc::MapZ - t - 1;
        }
        break;
      case 180:
        z = mc::MapZ - z - 1;
        x = mc::MapX - x - 1;
        break;
      case 90:
        {
          int t = x;
          x = z;
          z = mc::MapZ - t - 1;
        }
        break;
    };
  }

public:
  BlockRotation(settings_t *s, nbt::ByteArray *byte_array)
    : s(s), byte_array(byte_array) {}

  uint8_t get8(int x, int z, int y) {
    transform_xzy(x, z, y);

    assert(x >= 0 && x < mc::MapX);
    assert(z >= 0 && z < mc::MapZ);
    assert(y >= 0 && y < mc::MapY);
    int p = y + (z * mc::MapY + (x * mc::MapY * mc::MapZ));
    assert (p >= 0 && p < byte_array->length);
    return byte_array->values[p];
  }

  uint8_t get4(int x, int z, int y) {
    transform_xzy(x, z, y);

    assert(x >= 0 && x < mc::MapX);
    assert(z >= 0 && z < mc::MapZ);
    assert(y >= 0 && y < mc::MapY);
    int p = y + (z * mc::MapY + (x * mc::MapY * mc::MapZ));
    int ap = p / 2;

    assert (ap >= 0 && ap < byte_array->length);

    // force unsigned
    uint8_t bp = byte_array->values[ap] & 0xff;

    if (p % 2 == 0) {
      return bp >> 4;
    }
    else {
      return bp & 0xf;
    }
  }
};

ImageBuffer *Level::get_image(settings_t *s) {
  ImageBuffer *img = new ImageBuffer(mc::MapX, mc::MapZ, 1);

  if (!islevel) {
    return img;
  }

  img->set_reversed(true);

  // block type
  int bt;

  for (int x = 0, mz = mc::MapZ - 1; x < mc::MapX; x++, mz--) {
    for (int y = 0, mx = 0; y < mc::MapX; y++, mx++) {
      Color base(255, 255, 255, 0);

      bt = mc::Air;

      bool cave_initial = true;

      int my;

      BlockRotation blocks_r(s, blocks);
      BlockRotation blocklight_r(s, blocklight);
      BlockRotation skylight_r(s, skylight);

      // do incremental color fill until color is opaque
      for (my = s->top; my > s->bottom; my--) {
        bt = blocks_r.get8(mx, mz, my);

        if (s->cavemode && cavemode_ignore_block(s, mx, mz, my, bt, blocks, cave_initial)) {
          continue;
        }

        if (s->excludes[bt]) {
          continue;
        }

        Color bc(mc::MaterialColor[bt]);

        int bl = blocklight_r.get4(mx, mz, my);
        int sl = skylight_r.get4(mx, mz, my);

        apply_shading(s, bl, sl, 0, my, bc);

        base.underlay(bc);

        if (base.is_opaque()) {
          break;
        }
      }

      if (base.is_transparent()) {
        continue;
      }

      img->set_pixel(x, y, 0, base);
    }
  }

  return img;
}

ImageBuffer *Level::get_oblique_image(settings_t *s)
{
  ImageBuffer *img = new ImageBuffer(mc::MapX, mc::MapZ + mc::MapY, mc::MapY + mc::MapZ);

  if (!islevel) {
    return img;
  }

  Cube c(mc::MapX, mc::MapY, mc::MapZ);

  // block type
  int bt;

  for (int x = 0, mz = mc::MapZ - 1; x < mc::MapX; x++, mz--) {
    for (int y = 0, mx = 0; y < mc::MapX; y++, mx++) {
      bool cave_initial = true;
      int cavemode_top = s->top;

      if (s->cavemode) {
        for (int my = s->top; my > 0; my--) {
          bt = bget(blocks, mx, mz, y);

          if (!cavemode_isopen(bt)) {
            cavemode_top = my;
            break;
          }
        }
      }

      for (int my = s->bottom; my < s->top; my++) {
        point p(x, my, y);

        bt = bget(blocks, mx, mz, my);

        if (s->cavemode) {
          if (cavemode_ignore_block(s, mx, mz, my, bt, blocks, cave_initial) || my >= cavemode_top) {
            continue;
          }
        }

        if (s->excludes[bt]) {
          continue;
        }

        int bl = bsget(blocklight, mx, mz, my);
        int sl = bsget(skylight, mx, mz, my);

        int _px, _py;
        c.project_oblique(p, _px, _py);

        Color top(mc::MaterialColor[bt]);
        apply_shading(s, bl, sl, 0, my, top);
        img->add_pixel(_px, _py - 1, top);

        Color side(mc::MaterialSideColor[bt]);
        apply_shading(s, bl, sl, 0, my, side);
        img->add_pixel(_px, _py, side);
      }
    }
  }

  return img;
}

ImageBuffer *Level::get_obliqueangle_image(settings_t *s)
{
  ImageBuffer *img = new ImageBuffer(mc::MapX * 2 + 1, mc::MapX + mc::MapY + mc::MapZ, mc::MapY + mc::MapZ * 2);

  if (!islevel) {
    return img;
  }

  Cube c(mc::MapX, mc::MapY, mc::MapZ);

  // block type
  int bt;

  for (int z = 0; z < c.z; z++) {
    for (int x = 0; x < c.x; x++) {
      bool cave_initial = true;

      int cavemode_top = s->top;

      if (s->cavemode) {
        for (int y = s->top; y > 0; y--) {
          int _x = x, _z = z;
          transform_xz(s, _x, _z);

          bt = bget(blocks, _x, _z, y);

          if (!cavemode_isopen(bt)) {
            cavemode_top = y;
            break;
          }
        }
      }

      for (int y = s->bottom; y < s->top; y++) {
        point p(x, y, z);

        int _x = x, _z = z;
        transform_xz(s, _x, _z);

        bt = bget(blocks, _x, _z, y);

        if (s->cavemode) {
          if (cavemode_ignore_block(s, _x, _z, y, bt, blocks, cave_initial) || y >= cavemode_top) {
            continue;
          }
        }

        if (s->excludes[bt]) {
          continue;
        }

        int bl = bsget(blocklight, _x, _z, y);
        int sl = bsget(skylight, _x, _z, y);

        int _px, _py;
        c.project_obliqueangle(p, _px, _py);

        Color top(mc::MaterialColor[bt]);
        apply_shading(s, bl, sl, 0, y, top);
        Color side(mc::MaterialSideColor[bt]);
        apply_shading(s, bl, sl, 0, y, side);

        switch(mc::MaterialModes[bt]) {
        case mc::Block:
          img->add_pixel(_px, _py - 1, top);
          img->add_pixel(_px + 1, _py - 1, top);
          img->add_pixel(_px, _py, side);
          img->add_pixel(_px + 1, _py, side);
          break;
        case mc::HalfBlock:
          img->add_pixel(_px, _py, top);
          img->add_pixel(_px + 1, _py, top);
          break;
        }
      }
    }
  }

  return img;
}
<commit_msg>Finally, working rotation with proxy blocks and pre-calculating of new positions, CHECK IT OUT!<commit_after>#include <assert.h>

#include "global.h"

#include "Level.h"
#include "blocks.h"
#include "2d/cube.h"

void begin_compound(void *context, nbt::String name) {
  if (name.compare("Level") == 0) {
    ((Level*)context)->islevel = true;
  }
}

void register_int(void *context, nbt::String name, nbt::Int i) {
  if (name.compare("xPos") == 0) {
    ((Level *)context)->xPos = i;
    return;
  }

  if (name.compare("zPos") == 0) {
    ((Level *)context)->zPos = i;
    return;
  }
}

void register_byte_array(void *context, nbt::String name, nbt::ByteArray *byte_array) {
  if (name.compare("Blocks") == 0) {
    ((Level *)context)->blocks = byte_array;
    return;
  }

  if (name.compare("SkyLight") == 0) {
    ((Level *)context)->skylight = byte_array;
    return;
  }

  if (name.compare("HeightMap") == 0) {
    ((Level *)context)->heightmap = byte_array;
    return;
  }

  if (name.compare("BlockLight") == 0) {
    ((Level *)context)->blocklight = byte_array;
    return;
  }
}

void error_handler(void *context, size_t where, const char *why) {
  ((Level *)context)->grammar_error = true;
  ((Level *)context)->grammar_error_where = where;
  ((Level *)context)->grammar_error_why = why;
}

Level::~Level(){
  if (!ignore_blocks && islevel) {
    delete blocks;
    delete skylight;
    delete heightmap;
    delete blocklight;
  }
}

Level::Level(const char *path, bool ignore_blocks)
  : path(path), ignore_blocks(ignore_blocks)
{
  xPos = 0;
  zPos = 0;
  islevel = false;
  grammar_error = false;

  nbt::Parser parser(this);

  if (!ignore_blocks) {
    parser.register_byte_array = register_byte_array;
  }

  parser.register_int = register_int;
  parser.begin_compound = begin_compound;
  parser.error_handler = error_handler;

  try {
    parser.parse_file(path);
  } catch(nbt::bad_grammar &bg) {
    grammar_error = true;
  }
}

/**
 * Blocks[ z + ( y * ChunkSizeY(=128) + ( x * ChunkSizeY(=128) * ChunkSizeZ(=16) ) ) ];
 */
nbt::Byte bget(nbt::ByteArray *blocks, int x, int z, int y) {
  assert(x >= 0 && x < mc::MapX);
  assert(z >= 0 && z < mc::MapZ);
  assert(y >= 0 && y < mc::MapY);
  int p = y + (z * mc::MapY + (x * mc::MapY * mc::MapZ));
  assert (p >= 0 && p < blocks->length);
  return blocks->values[p];
}

nbt::Byte bsget(nbt::ByteArray *skylight, int x, int z, int y) {
  assert(x >= 0 && x < mc::MapX);
  assert(z >= 0 && z < mc::MapZ);
  assert(y >= 0 && y < mc::MapY);
  int p = y + (z * mc::MapY + (x * mc::MapY * mc::MapZ));
  int ap = p / 2;

  assert (ap >= 0 && ap < skylight->length);

  // force unsigned
  uint8_t bp = skylight->values[ap] & 0xff;

  if (p % 2 == 0) {
    return bp >> 4;
  }
  else {
    return bp & 0xf;
  }
}

void transform_xz(settings_t *s, int &x, int &z) {
  /*if (s->flip) {
    int t = x;
    x = z;
    z = mc::MapZ - t - 1;
  }

  if (s->invert) {
    z = mc::MapZ - z - 1;
    x = mc::MapX - x - 1;
  }*/
}

inline void apply_shading(settings_t *s, int bl, int sl, int hm, int y, Color &c) {
  // if night, darken all colors not emitting light
  if (s->night) {
    c.darken((0xd0 * (16 - bl)) / 16);
  }

  c.darken((mc::MapY - y));

  c.darken(sl);
}

inline bool cavemode_isopen(int bt) {
  switch(bt) {
    case mc::Air: return true;
    case mc::Leaves: return true;
    default: return false;
  }
}

inline bool cavemode_ignore_block(settings_t *s, int x, int z, int y, int bt, nbt::ByteArray *blocks, bool &cave_initial) {
  if (cave_initial) {
    if (!cavemode_isopen(bt)) {
      cave_initial = false;
      return true;
    }

    return true;
  }

  if (!cavemode_isopen(bt)) {
    if (y < s->top && cavemode_isopen(bget(blocks, x, z, y + 1))) {
      return false;
    }
  }

  return true;
}

class BlockRotation {
private:
  settings_t *s;
  nbt::ByteArray *byte_array;

  void transform_xzy(int& x, int& z, int& y) {
    switch (s->rotation) {
      case 270:
        z = mc::MapZ - z - 1;
        x = mc::MapX - x - 1;
        {
          int t = x;
          x = z;
          z = mc::MapZ - t - 1;
        }
        break;
      case 180:
        z = mc::MapZ - z - 1;
        x = mc::MapX - x - 1;
        break;
      case 90:
        {
          int t = x;
          x = z;
          z = mc::MapZ - t - 1;
        }
        break;
    };
  }

public:
  BlockRotation(settings_t *s, nbt::ByteArray *byte_array)
    : s(s), byte_array(byte_array) {}

  uint8_t get8(int x, int z, int y) {
    transform_xzy(x, z, y);

    assert(x >= 0 && x < mc::MapX);
    assert(z >= 0 && z < mc::MapZ);
    assert(y >= 0 && y < mc::MapY);
    int p = y + (z * mc::MapY + (x * mc::MapY * mc::MapZ));
    assert (p >= 0 && p < byte_array->length);
    return byte_array->values[p];
  }

  uint8_t get4(int x, int z, int y) {
    transform_xzy(x, z, y);

    assert(x >= 0 && x < mc::MapX);
    assert(z >= 0 && z < mc::MapZ);
    assert(y >= 0 && y < mc::MapY);
    int p = y + (z * mc::MapY + (x * mc::MapY * mc::MapZ));
    int ap = p / 2;

    assert (ap >= 0 && ap < byte_array->length);

    // force unsigned
    uint8_t bp = byte_array->values[ap] & 0xff;

    if (p % 2 == 0) {
      return bp >> 4;
    }
    else {
      return bp & 0xf;
    }
  }
};

ImageBuffer *Level::get_image(settings_t *s) {
  ImageBuffer *img = new ImageBuffer(mc::MapX, mc::MapZ, 1);

  if (!islevel) {
    return img;
  }

  img->set_reversed(true);

  // block type
  int bt;

  for (int x = 0, mz = mc::MapZ - 1; x < mc::MapX; x++, mz--) {
    for (int y = 0, mx = 0; y < mc::MapX; y++, mx++) {
      Color base(255, 255, 255, 0);

      bt = mc::Air;

      bool cave_initial = true;

      int my;

      BlockRotation blocks_r(s, blocks);
      BlockRotation blocklight_r(s, blocklight);
      BlockRotation skylight_r(s, skylight);

      // do incremental color fill until color is opaque
      for (my = s->top; my > s->bottom; my--) {
        bt = blocks_r.get8(mx, mz, my);

        if (s->cavemode && cavemode_ignore_block(s, mx, mz, my, bt, blocks, cave_initial)) {
          continue;
        }

        if (s->excludes[bt]) {
          continue;
        }

        Color bc(mc::MaterialColor[bt]);

        int bl = blocklight_r.get4(mx, mz, my);
        int sl = skylight_r.get4(mx, mz, my);

        apply_shading(s, bl, sl, 0, my, bc);

        base.underlay(bc);

        if (base.is_opaque()) {
          break;
        }
      }

      if (base.is_transparent()) {
        continue;
      }

      img->set_pixel(x, y, 0, base);
    }
  }

  return img;
}

ImageBuffer *Level::get_oblique_image(settings_t *s)
{
  ImageBuffer *img = new ImageBuffer(mc::MapX, mc::MapZ + mc::MapY, mc::MapY + mc::MapZ);

  if (!islevel) {
    return img;
  }

  Cube c(mc::MapX, mc::MapY, mc::MapZ);

  // block type
  int bt;

  BlockRotation blocks_r(s, blocks);
  BlockRotation blocklight_r(s, blocklight);
  BlockRotation skylight_r(s, skylight);

  for (int x = 0, mz = mc::MapZ - 1; x < mc::MapX; x++, mz--) {
    for (int y = 0, mx = 0; y < mc::MapX; y++, mx++) {
      bool cave_initial = true;
      int cavemode_top = s->top;

      if (s->cavemode) {
        for (int my = s->top; my > 0; my--) {
          bt = blocks_r.get8(mx, mz, y);

          if (!cavemode_isopen(bt)) {
            cavemode_top = my;
            break;
          }
        }
      }

      for (int my = s->bottom; my < s->top; my++) {
        point p(x, my, y);

        bt = blocks_r.get8(mx, mz, my);

        if (s->cavemode) {
          if (cavemode_ignore_block(s, mx, mz, my, bt, blocks, cave_initial) || my >= cavemode_top) {
            continue;
          }
        }

        if (s->excludes[bt]) {
          continue;
        }

        int bl = blocklight_r.get4(mx, mz, my);
        int sl = skylight_r.get4(mx, mz, my);

        int _px, _py;
        c.project_oblique(p, _px, _py);

        Color top(mc::MaterialColor[bt]);
        apply_shading(s, bl, sl, 0, my, top);
        img->add_pixel(_px, _py - 1, top);

        Color side(mc::MaterialSideColor[bt]);
        apply_shading(s, bl, sl, 0, my, side);
        img->add_pixel(_px, _py, side);
      }
    }
  }

  return img;
}

ImageBuffer *Level::get_obliqueangle_image(settings_t *s)
{
  ImageBuffer *img = new ImageBuffer(mc::MapX * 2 + 1, mc::MapX + mc::MapY + mc::MapZ, mc::MapY + mc::MapZ * 2);

  if (!islevel) {
    return img;
  }

  Cube c(mc::MapX, mc::MapY, mc::MapZ);

  // block type
  int bt;

  BlockRotation blocks_r(s, blocks);
  BlockRotation blocklight_r(s, blocklight);
  BlockRotation skylight_r(s, skylight);

  for (int z = 0; z < c.z; z++) {
    for (int x = 0; x < c.x; x++) {
      bool cave_initial = true;

      int cavemode_top = s->top;

      if (s->cavemode) {
        for (int y = s->top; y > 0; y--) {
          int _x = x, _z = z;
          transform_xz(s, _x, _z);

          bt = blocks_r.get8(_x, _z, y);

          if (!cavemode_isopen(bt)) {
            cavemode_top = y;
            break;
          }
        }
      }

      for (int y = s->bottom; y < s->top; y++) {
        point p(x, y, z);

        int _x = x, _z = z;
        transform_xz(s, _x, _z);

        bt = blocks_r.get8(_x, _z, y);

        if (s->cavemode) {
          if (cavemode_ignore_block(s, _x, _z, y, bt, blocks, cave_initial) || y >= cavemode_top) {
            continue;
          }
        }

        if (s->excludes[bt]) {
          continue;
        }

        int bl = skylight_r.get4(_x, _z, y);
        int sl = blocklight_r.get4(_x, _z, y);

        int _px, _py;
        c.project_obliqueangle(p, _px, _py);

        Color top(mc::MaterialColor[bt]);
        apply_shading(s, bl, sl, 0, y, top);
        Color side(mc::MaterialSideColor[bt]);
        apply_shading(s, bl, sl, 0, y, side);

        switch(mc::MaterialModes[bt]) {
        case mc::Block:
          img->add_pixel(_px, _py - 1, top);
          img->add_pixel(_px + 1, _py - 1, top);
          img->add_pixel(_px, _py, side);
          img->add_pixel(_px + 1, _py, side);
          break;
        case mc::HalfBlock:
          img->add_pixel(_px, _py, top);
          img->add_pixel(_px + 1, _py, top);
          break;
        }
      }
    }
  }

  return img;
}
<|endoftext|>


<commit_before>// Scintilla source code edit control
/** @file LexVB.cxx
 ** Lexer for Visual Basic and VBScript.
 **/
// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdarg.h>

#include "Platform.h"

#include "PropSet.h"
#include "Accessor.h"
#include "StyleContext.h"
#include "KeyWords.h"
#include "Scintilla.h"
#include "SciLexer.h"

static bool IsVBComment(Accessor &styler, int pos, int len) {
	return len>0 && styler[pos]=='\'';
}

inline bool IsTypeCharacter(const int ch) {
	return ch == '%' || ch == '&' || ch == '@' || ch == '!' || ch == '#' || ch == '$';
}

inline bool IsAWordChar(const int ch) {
	return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_');
}

inline bool IsAWordStart(const int ch) {
	return (ch < 0x80) && (isalnum(ch) || ch == '_');
}

static void ColouriseVBDoc(unsigned int startPos, int length, int initStyle,
                           WordList *keywordlists[], Accessor &styler, bool vbScriptSyntax) {

	WordList &keywords = *keywordlists[0];

	styler.StartAt(startPos);

	int visibleChars = 0;

	StyleContext sc(startPos, length, initStyle, styler);

	for (; sc.More(); sc.Forward()) {

		if (sc.state == SCE_B_OPERATOR) {
			sc.SetState(SCE_B_DEFAULT);
		} else if (sc.state == SCE_B_KEYWORD) {
			if (!IsAWordChar(sc.ch)) {
				if (vbScriptSyntax || !IsTypeCharacter(sc.ch)) {
					if (sc.ch == ']')
						sc.Forward();
					char s[100];
					sc.GetCurrentLowered(s, sizeof(s));
					if (keywords.InList(s)) {
						if (strcmp(s, "rem") == 0) {
							sc.ChangeState(SCE_B_COMMENT);
							if (sc.atLineEnd) {
								sc.SetState(SCE_B_DEFAULT);
							}
						} else {
							sc.SetState(SCE_B_DEFAULT);
						}
					} else {
						sc.ChangeState(SCE_B_IDENTIFIER);
						sc.SetState(SCE_B_DEFAULT);
					}
				}
			}
		} else if (sc.state == SCE_B_NUMBER) {
			if (!IsAWordChar(sc.ch)) {
				sc.SetState(SCE_B_DEFAULT);
			}
		} else if (sc.state == SCE_B_STRING) {
			// VB doubles quotes to preserve them, so just end this string
			// state now as a following quote will start again
			if (sc.ch == '\"') {
				if (tolower(sc.chNext) == 'c') {
					sc.Forward();
				}
				sc.ForwardSetState(SCE_B_DEFAULT);
			}
		} else if (sc.state == SCE_B_COMMENT) {
			if (sc.atLineEnd) {
				sc.SetState(SCE_B_DEFAULT);
			}
		} else if (sc.state == SCE_B_PREPROCESSOR) {
			if (sc.atLineEnd) {
				sc.SetState(SCE_B_DEFAULT);
			}
		} else if (sc.state == SCE_B_DATE) {
			if (sc.ch == '#') {
				sc.ForwardSetState(SCE_B_DEFAULT);
			}
		}

		if (sc.state == SCE_B_DEFAULT) {
			if (sc.ch == '\'') {
				sc.SetState(SCE_B_COMMENT);
			} else if (sc.ch == '\"') {
				sc.SetState(SCE_B_STRING);
			} else if (sc.ch == '#' && visibleChars == 0) {
				// Preprocessor commands are alone on their line
				sc.SetState(SCE_B_PREPROCESSOR);
			} else if (sc.ch == '#') {
				int n = 1;
				int chSeek = ' ';
				while (chSeek == ' ' || chSeek == '\t') {
					chSeek = sc.GetRelative(n);
					n++;
				}
				if (IsADigit(chSeek)) {
					sc.SetState(SCE_B_DATE);
				} else {
					sc.SetState(SCE_B_OPERATOR);
				}
			} else if (sc.ch == '&' && tolower(sc.chNext) == 'h') {
				sc.SetState(SCE_B_NUMBER);
			} else if (sc.ch == '&' && tolower(sc.chNext) == 'o') {
				sc.SetState(SCE_B_NUMBER);
			} else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
				sc.SetState(SCE_B_NUMBER);
			} else if (IsAWordStart(sc.ch) || (sc.ch == '[')) {
				sc.SetState(SCE_B_KEYWORD);
			} else if (isoperator(static_cast<char>(sc.ch)) || (sc.ch == '\\')) {
				sc.SetState(SCE_B_OPERATOR);
			}
		}

		if (sc.atLineEnd) {
			visibleChars = 0;
		}
		if (!IsASpace(sc.ch)) {
			visibleChars++;
		}
	}
	sc.Complete();
}

static void FoldVBDoc(unsigned int startPos, int length, int,
						   WordList *[], Accessor &styler) {
	int endPos = startPos + length;

	// Backtrack to previous line in case need to fix its fold status
	int lineCurrent = styler.GetLine(startPos);
	if (startPos > 0) {
		if (lineCurrent > 0) {
			lineCurrent--;
			startPos = styler.LineStart(lineCurrent);
		}
	}
	int spaceFlags = 0;
	int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, IsVBComment);
	char chNext = styler[startPos];
	for (int i = startPos; i < endPos; i++) {
		char ch = chNext;
		chNext = styler.SafeGetCharAt(i + 1);

		if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == endPos)) {
			int lev = indentCurrent;
			int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags, IsVBComment);
			if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) {
				// Only non whitespace lines can be headers
				if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK)) {
					lev |= SC_FOLDLEVELHEADERFLAG;
				} else if (indentNext & SC_FOLDLEVELWHITEFLAG) {
					// Line after is blank so check the next - maybe should continue further?
					int spaceFlags2 = 0;
					int indentNext2 = styler.IndentAmount(lineCurrent + 2, &spaceFlags2, IsVBComment);
					if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext2 & SC_FOLDLEVELNUMBERMASK)) {
						lev |= SC_FOLDLEVELHEADERFLAG;
					}
				}
			}
			indentCurrent = indentNext;
			styler.SetLevel(lineCurrent, lev);
			lineCurrent++;
		}
	}
}

static void ColouriseVBNetDoc(unsigned int startPos, int length, int initStyle,
                           WordList *keywordlists[], Accessor &styler) {
	ColouriseVBDoc(startPos, length, initStyle, keywordlists, styler, false);
}

static void ColouriseVBScriptDoc(unsigned int startPos, int length, int initStyle,
                           WordList *keywordlists[], Accessor &styler) {
	ColouriseVBDoc(startPos, length, initStyle, keywordlists, styler, true);
}

LexerModule lmVB(SCLEX_VB, ColouriseVBNetDoc, "vb", FoldVBDoc);
LexerModule lmVBScript(SCLEX_VBSCRIPT, ColouriseVBScriptDoc, "vbscript", FoldVBDoc);

<commit_msg>Made file handle constants in statements such as "close #1" work by styling them in the date literal style.<commit_after>// Scintilla source code edit control
/** @file LexVB.cxx
 ** Lexer for Visual Basic and VBScript.
 **/
// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdarg.h>

#include "Platform.h"

#include "PropSet.h"
#include "Accessor.h"
#include "StyleContext.h"
#include "KeyWords.h"
#include "Scintilla.h"
#include "SciLexer.h"

static bool IsVBComment(Accessor &styler, int pos, int len) {
	return len>0 && styler[pos]=='\'';
}

inline bool IsTypeCharacter(const int ch) {
	return ch == '%' || ch == '&' || ch == '@' || ch == '!' || ch == '#' || ch == '$';
}

inline bool IsAWordChar(const int ch) {
	return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_');
}

inline bool IsAWordStart(const int ch) {
	return (ch < 0x80) && (isalnum(ch) || ch == '_');
}

inline bool IsADateCharacter(const int ch) {
	return (ch < 0x80) &&
		(isalnum(ch) || ch == '|' || ch == '-' || ch == '/' || ch == ':' || ch == ' ' || ch == '\t');
}

static void ColouriseVBDoc(unsigned int startPos, int length, int initStyle,
                           WordList *keywordlists[], Accessor &styler, bool vbScriptSyntax) {

	WordList &keywords = *keywordlists[0];

	styler.StartAt(startPos);

	int visibleChars = 0;

	StyleContext sc(startPos, length, initStyle, styler);

	for (; sc.More(); sc.Forward()) {

		if (sc.state == SCE_B_OPERATOR) {
			sc.SetState(SCE_B_DEFAULT);
		} else if (sc.state == SCE_B_KEYWORD) {
			if (!IsAWordChar(sc.ch)) {
				if (vbScriptSyntax || !IsTypeCharacter(sc.ch)) {
					if (sc.ch == ']')
						sc.Forward();
					char s[100];
					sc.GetCurrentLowered(s, sizeof(s));
					if (keywords.InList(s)) {
						if (strcmp(s, "rem") == 0) {
							sc.ChangeState(SCE_B_COMMENT);
							if (sc.atLineEnd) {
								sc.SetState(SCE_B_DEFAULT);
							}
						} else {
							sc.SetState(SCE_B_DEFAULT);
						}
					} else {
						sc.ChangeState(SCE_B_IDENTIFIER);
						sc.SetState(SCE_B_DEFAULT);
					}
				}
			}
		} else if (sc.state == SCE_B_NUMBER) {
			if (!IsAWordChar(sc.ch)) {
				sc.SetState(SCE_B_DEFAULT);
			}
		} else if (sc.state == SCE_B_STRING) {
			// VB doubles quotes to preserve them, so just end this string
			// state now as a following quote will start again
			if (sc.ch == '\"') {
				if (tolower(sc.chNext) == 'c') {
					sc.Forward();
				}
				sc.ForwardSetState(SCE_B_DEFAULT);
			}
		} else if (sc.state == SCE_B_COMMENT) {
			if (sc.atLineEnd) {
				sc.SetState(SCE_B_DEFAULT);
			}
		} else if (sc.state == SCE_B_PREPROCESSOR) {
			if (sc.atLineEnd) {
				sc.SetState(SCE_B_DEFAULT);
			}
		} else if (sc.state == SCE_B_DATE) {
			if (sc.ch == '#' || !IsADateCharacter(sc.chNext)) {
				sc.ForwardSetState(SCE_B_DEFAULT);
			}
		}

		if (sc.state == SCE_B_DEFAULT) {
			if (sc.ch == '\'') {
				sc.SetState(SCE_B_COMMENT);
			} else if (sc.ch == '\"') {
				sc.SetState(SCE_B_STRING);
			} else if (sc.ch == '#' && visibleChars == 0) {
				// Preprocessor commands are alone on their line
				sc.SetState(SCE_B_PREPROCESSOR);
			} else if (sc.ch == '#') {
				int n = 1;
				int chSeek = ' ';
				while (chSeek == ' ' || chSeek == '\t') {
					chSeek = sc.GetRelative(n);
					n++;
				}
				if (IsADigit(chSeek)) {
					sc.SetState(SCE_B_DATE);
				} else {
					sc.SetState(SCE_B_OPERATOR);
				}
			} else if (sc.ch == '&' && tolower(sc.chNext) == 'h') {
				sc.SetState(SCE_B_NUMBER);
			} else if (sc.ch == '&' && tolower(sc.chNext) == 'o') {
				sc.SetState(SCE_B_NUMBER);
			} else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
				sc.SetState(SCE_B_NUMBER);
			} else if (IsAWordStart(sc.ch) || (sc.ch == '[')) {
				sc.SetState(SCE_B_KEYWORD);
			} else if (isoperator(static_cast<char>(sc.ch)) || (sc.ch == '\\')) {
				sc.SetState(SCE_B_OPERATOR);
			}
		}

		if (sc.atLineEnd) {
			visibleChars = 0;
		}
		if (!IsASpace(sc.ch)) {
			visibleChars++;
		}
	}
	sc.Complete();
}

static void FoldVBDoc(unsigned int startPos, int length, int,
						   WordList *[], Accessor &styler) {
	int endPos = startPos + length;

	// Backtrack to previous line in case need to fix its fold status
	int lineCurrent = styler.GetLine(startPos);
	if (startPos > 0) {
		if (lineCurrent > 0) {
			lineCurrent--;
			startPos = styler.LineStart(lineCurrent);
		}
	}
	int spaceFlags = 0;
	int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, IsVBComment);
	char chNext = styler[startPos];
	for (int i = startPos; i < endPos; i++) {
		char ch = chNext;
		chNext = styler.SafeGetCharAt(i + 1);

		if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == endPos)) {
			int lev = indentCurrent;
			int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags, IsVBComment);
			if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) {
				// Only non whitespace lines can be headers
				if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK)) {
					lev |= SC_FOLDLEVELHEADERFLAG;
				} else if (indentNext & SC_FOLDLEVELWHITEFLAG) {
					// Line after is blank so check the next - maybe should continue further?
					int spaceFlags2 = 0;
					int indentNext2 = styler.IndentAmount(lineCurrent + 2, &spaceFlags2, IsVBComment);
					if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext2 & SC_FOLDLEVELNUMBERMASK)) {
						lev |= SC_FOLDLEVELHEADERFLAG;
					}
				}
			}
			indentCurrent = indentNext;
			styler.SetLevel(lineCurrent, lev);
			lineCurrent++;
		}
	}
}

static void ColouriseVBNetDoc(unsigned int startPos, int length, int initStyle,
                           WordList *keywordlists[], Accessor &styler) {
	ColouriseVBDoc(startPos, length, initStyle, keywordlists, styler, false);
}

static void ColouriseVBScriptDoc(unsigned int startPos, int length, int initStyle,
                           WordList *keywordlists[], Accessor &styler) {
	ColouriseVBDoc(startPos, length, initStyle, keywordlists, styler, true);
}

LexerModule lmVB(SCLEX_VB, ColouriseVBNetDoc, "vb", FoldVBDoc);
LexerModule lmVBScript(SCLEX_VBSCRIPT, ColouriseVBScriptDoc, "vbscript", FoldVBDoc);

<|endoftext|>


<commit_before>#define DEBUG 1
/**
 * File    : B.cpp
 * Author  : Kazune Takahashi
 * Created : 11/9/2019, 9:04:32 PM
 * Powered by Visual Studio Code
 */
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <vector>
#include <string>
#include <complex>
#include <tuple>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <bitset>
#include <functional>
#include <random>
#include <chrono>
#include <cctype>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib>
using namespace std;
template <typename T>
void ch_max(T &left, T right)
{
  if (left < right)
  {
    left = right;
  }
}
template <typename T>
void ch_min(T &left, T right)
{
  if (left > right)
  {
    left = right;
  }
}
using ll = long long;
constexpr ll MOD{1000000007LL};
constexpr ll MAX_SIZE{3000010LL};
template <ll MOD = MOD>
class Mint
{
public:
  ll x;
  Mint() : x{0LL} {}
  Mint(ll x) : x{x % MOD} {}
  Mint operator-() const { return x ? MOD - x : 0; }
  Mint &operator+=(const Mint &a)
  {
    if ((x += a.x) >= MOD)
    {
      x -= MOD;
    }
    return *this;
  }
  Mint &operator-=(const Mint &a) { return *this += -a; }
  Mint &operator*=(const Mint &a)
  {
    (x *= a.x) %= MOD;
    return *this;
  }
  Mint &operator/=(const Mint &a)
  {
    Mint b{a};
    return *this *= b.power(MOD - 2);
  }
  Mint operator+(const Mint &a) const { return Mint(*this) += a; }
  Mint operator-(const Mint &a) const { return Mint(*this) -= a; }
  Mint operator*(const Mint &a) const { return Mint(*this) *= a; }
  Mint operator/(const Mint &a) const { return Mint(*this) /= a; }
  bool operator<(const Mint &a) const { return x < a.x; }
  bool operator==(const Mint &a) const { return x == a.x; }
  const Mint power(ll N)
  {
    if (N == 0)
    {
      return 1;
    }
    else if (N % 2 == 1)
    {
      return *this * power(N - 1);
    }
    else
    {
      Mint half = power(N / 2);
      return half * half;
    }
  }
};
template <ll MOD>
Mint<MOD> operator+(ll lhs, const Mint<MOD> &rhs)
{
  return rhs + lhs;
}
template <ll MOD>
Mint<MOD> operator-(ll lhs, const Mint<MOD> &rhs)
{
  return -rhs + lhs;
}
template <ll MOD>
Mint<MOD> operator*(ll lhs, const Mint<MOD> &rhs)
{
  return rhs * lhs;
}
template <ll MOD>
Mint<MOD> operator/(ll lhs, const Mint<MOD> &rhs)
{
  return Mint<MOD>{lhs} / rhs;
}
template <ll MOD>
istream &operator>>(istream &stream, Mint<MOD> &a)
{
  return stream >> a.x;
}
template <ll MOD>
ostream &operator<<(ostream &stream, const Mint<MOD> &a)
{
  return stream << a.x;
}
template <ll MOD = MOD, ll MAX_SIZE = MAX_SIZE>
class Combination
{
public:
  vector<Mint<MOD>> inv, fact, factinv;
  Combination() : inv(MAX_SIZE), fact(MAX_SIZE), factinv(MAX_SIZE)
  {
    inv[1] = 1;
    for (auto i = 2LL; i < MAX_SIZE; i++)
    {
      inv[i] = (-inv[MOD % i]) * (MOD / i);
    }
    fact[0] = factinv[0] = 1;
    for (auto i = 1LL; i < MAX_SIZE; i++)
    {
      fact[i] = Mint<MOD>(i) * fact[i - 1];
      factinv[i] = inv[i] * factinv[i - 1];
    }
  }
  Mint<MOD> operator()(int n, int k)
  {
    if (n >= 0 && k >= 0 && n - k >= 0)
    {
      return fact[n] * factinv[k] * factinv[n - k];
    }
    return 0;
  }
  Mint<MOD> catalan(int x, int y)
  {
    return (*this)(x + y, y) - (*this)(x + y, y - 1);
  }
};
// for C++14
using mint = Mint<MOD>;
using combination = Combination<MOD, MAX_SIZE>;
ll gcd(ll x, ll y) { return y ? gcd(y, x % y) : x; }
// constexpr double epsilon = 1e-10;
// constexpr ll infty = 1000000000000000LL;
// constexpr int dx[4] = {1, 0, -1, 0};
// constexpr int dy[4] = {0, 1, 0, -1};
void Yes()
{
  cout << "Yes" << endl;
  exit(0);
}
void No()
{
  cout << 0 << endl;
  exit(0);
}

int main()
{
  ll N;
  vector<ll> D(N);
  for (auto i = 0; i < N; i++)
  {
    cin >> D[i];
  }
  vector<ll> A(N, 0LL);
  if (D[0] != 0)
  {
    No();
  }
  A[0] = 1;
  for (auto i = 1; i < N; i++)
  {
    if (D[i] == 0)
    {
      No();
    }
    A[D[i]]++;
  }
  mint ans{1LL};
  ll sum{1LL};
  for (auto i = 1; i < N; i++)
  {
    ans *= mint{A[i - 1]}.power(A[i]);
    sum += A[i];
    if (sum == N)
    {
      break;
    }
  }
  cout << ans << endl;
}<commit_msg>submit B.cpp to 'B - Counting of Trees' (nikkei2019-2-qual) [C++14 (GCC 5.4.1)]<commit_after>#define DEBUG 1
/**
 * File    : B.cpp
 * Author  : Kazune Takahashi
 * Created : 11/9/2019, 9:04:32 PM
 * Powered by Visual Studio Code
 */
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <vector>
#include <string>
#include <complex>
#include <tuple>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <bitset>
#include <functional>
#include <random>
#include <chrono>
#include <cctype>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib>
using namespace std;
template <typename T>
void ch_max(T &left, T right)
{
  if (left < right)
  {
    left = right;
  }
}
template <typename T>
void ch_min(T &left, T right)
{
  if (left > right)
  {
    left = right;
  }
}
using ll = long long;
constexpr ll MOD{1000000007LL};
constexpr ll MAX_SIZE{3000010LL};
template <ll MOD = MOD>
class Mint
{
public:
  ll x;
  Mint() : x{0LL} {}
  Mint(ll x) : x{x % MOD} {}
  Mint operator-() const { return x ? MOD - x : 0; }
  Mint &operator+=(const Mint &a)
  {
    if ((x += a.x) >= MOD)
    {
      x -= MOD;
    }
    return *this;
  }
  Mint &operator-=(const Mint &a) { return *this += -a; }
  Mint &operator*=(const Mint &a)
  {
    (x *= a.x) %= MOD;
    return *this;
  }
  Mint &operator/=(const Mint &a)
  {
    Mint b{a};
    return *this *= b.power(MOD - 2);
  }
  Mint operator+(const Mint &a) const { return Mint(*this) += a; }
  Mint operator-(const Mint &a) const { return Mint(*this) -= a; }
  Mint operator*(const Mint &a) const { return Mint(*this) *= a; }
  Mint operator/(const Mint &a) const { return Mint(*this) /= a; }
  bool operator<(const Mint &a) const { return x < a.x; }
  bool operator==(const Mint &a) const { return x == a.x; }
  const Mint power(ll N)
  {
    if (N == 0)
    {
      return 1;
    }
    else if (N % 2 == 1)
    {
      return *this * power(N - 1);
    }
    else
    {
      Mint half = power(N / 2);
      return half * half;
    }
  }
};
template <ll MOD>
Mint<MOD> operator+(ll lhs, const Mint<MOD> &rhs)
{
  return rhs + lhs;
}
template <ll MOD>
Mint<MOD> operator-(ll lhs, const Mint<MOD> &rhs)
{
  return -rhs + lhs;
}
template <ll MOD>
Mint<MOD> operator*(ll lhs, const Mint<MOD> &rhs)
{
  return rhs * lhs;
}
template <ll MOD>
Mint<MOD> operator/(ll lhs, const Mint<MOD> &rhs)
{
  return Mint<MOD>{lhs} / rhs;
}
template <ll MOD>
istream &operator>>(istream &stream, Mint<MOD> &a)
{
  return stream >> a.x;
}
template <ll MOD>
ostream &operator<<(ostream &stream, const Mint<MOD> &a)
{
  return stream << a.x;
}
template <ll MOD = MOD, ll MAX_SIZE = MAX_SIZE>
class Combination
{
public:
  vector<Mint<MOD>> inv, fact, factinv;
  Combination() : inv(MAX_SIZE), fact(MAX_SIZE), factinv(MAX_SIZE)
  {
    inv[1] = 1;
    for (auto i = 2LL; i < MAX_SIZE; i++)
    {
      inv[i] = (-inv[MOD % i]) * (MOD / i);
    }
    fact[0] = factinv[0] = 1;
    for (auto i = 1LL; i < MAX_SIZE; i++)
    {
      fact[i] = Mint<MOD>(i) * fact[i - 1];
      factinv[i] = inv[i] * factinv[i - 1];
    }
  }
  Mint<MOD> operator()(int n, int k)
  {
    if (n >= 0 && k >= 0 && n - k >= 0)
    {
      return fact[n] * factinv[k] * factinv[n - k];
    }
    return 0;
  }
  Mint<MOD> catalan(int x, int y)
  {
    return (*this)(x + y, y) - (*this)(x + y, y - 1);
  }
};
// for C++14
using mint = Mint<MOD>;
using combination = Combination<MOD, MAX_SIZE>;
ll gcd(ll x, ll y) { return y ? gcd(y, x % y) : x; }
// constexpr double epsilon = 1e-10;
// constexpr ll infty = 1000000000000000LL;
// constexpr int dx[4] = {1, 0, -1, 0};
// constexpr int dy[4] = {0, 1, 0, -1};
void Yes()
{
  cout << "Yes" << endl;
  exit(0);
}
void No()
{
  cout << 0 << endl;
  exit(0);
}

int main()
{
  ll N;
  cin >> N;
  vector<ll> D(N);
  for (auto i = 0; i < N; i++)
  {
    cin >> D[i];
  }
  vector<ll> A(N, 0LL);
  if (D[0] != 0)
  {
    No();
  }
  A[0] = 1;
  for (auto i = 1; i < N; i++)
  {
    if (D[i] == 0)
    {
      No();
    }
    A[D[i]]++;
  }
  mint ans{1LL};
  ll sum{1LL};
  for (auto i = 1; i < N; i++)
  {
    ans *= mint{A[i - 1]}.power(A[i]);
    sum += A[i];
    if (sum == N)
    {
      break;
    }
  }
  cout << ans << endl;
}<|endoftext|>


<commit_before><commit_msg>Fix invalid string access<commit_after><|endoftext|>


<commit_before>/* Copyright 2009 SPARTA, Inc., dba Cobham Analytic Solutions
 *
 * This file is part of WATCHER.
 *
 *     WATCHER is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Affero General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     WATCHER is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Affero General Public License for more details.
 *
 *     You should have received a copy of the GNU Affero General Public License
 *     along with Watcher.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <iostream>
#include <string>
#include <getopt.h>

#include "server.h"
#include "logger.h"
#include "libconfig.h++"
#include "initConfig.h"
#include "singletonConfig.h"
#include "watcherd.h"

using namespace std;
using namespace watcher;
using namespace libconfig;

option Options[] = {
    { "help", 0, NULL, 'h' },
    { "config", 1, NULL, 'c' },
    { "read-only", 1, NULL, 'r' },
    { 0, 0, NULL, 0 }
};

void usage(const char *progName, bool exitp)
{
    cout << "Usage: " << basename(progName) << " [-c config filename]" << endl;
    cout << "Args: " << endl;
    cout << "   -h, show this messsage and exit." << endl;
    cout << "   -c configfile - If not given a filename of the form \""<< basename(progName) << ".cfg\" is assumed." << endl;
    cout << "   -r, --read-only - do not write events to the database." << endl;
    cout << "If a configuration file is not found on startup, a default one will be created, used, and saved on program exit." << endl;

    if (exitp)
        exit(EXIT_FAILURE);
}

DECLARE_GLOBAL_LOGGER("watcherdMain");

int main(int argc, char* argv[])
{
    TRACE_ENTER();

    int i;
    bool readOnly = false;
    while ((i = getopt_long(argc, argv, "hc:r", Options, NULL)) != -1) {
        switch (i) {
            case 'c':
                //handled below
                break;
            case 'r':
                readOnly = true;
                break;
            default:
                usage(argv[0], true);
        }
    }

    string configFilename;
    Config &config=SingletonConfig::instance();
    SingletonConfig::lock();
    if (false==initConfig(config, argc, argv, configFilename))
    {
        cerr << "Error reading configuration file, unable to continue." << endl;
        cerr << "Usage: " << basename(argv[0]) << " [-c|--configFile] configfile" << endl;
        return 1;
    }
    SingletonConfig::unlock();

    string logConf("log.properties");
    if (!config.lookupValue("logPropertiesFile", logConf))
    {
        cout << "Unable to find logPropertiesFile setting in the configuration file, using default: " << logConf
             << " and adding it to the configuration file." << endl;
        config.getRoot().add("logPropertiesFile", libconfig::Setting::TypeString)=logConf;
    }

    LOAD_LOG_PROPS(logConf);

    LOG_INFO("Logger initialized from file \"" << logConf << "\"");

    string address("glory");
    string port("8095");
    size_t numThreads=8;
    std::string dbPath("event.db");

    if (!config.lookupValue("server", address))
    {
        LOG_INFO("'server' not found in the configuration file, using default: " << address
                << " and adding this to the configuration file.");
        config.getRoot().add("server", libconfig::Setting::TypeString) = address;
    }

    if (!config.lookupValue("port", port))
    {
        LOG_INFO("'port' not found in the configuration file, using default: " << port
                << " and adding this to the configuration file.");
        config.getRoot().add("port", libconfig::Setting::TypeString)=port;
    }

    if (!config.lookupValue("serverThreadNum", numThreads))
    {
        LOG_INFO("'serverThreadNum' not found in the configuration file, using default: " << numThreads
                << " and adding this to the configuration file.");
           config.getRoot().add("serverThreadNum", libconfig::Setting::TypeInt)=static_cast<int>(numThreads);
    }

    if (!config.lookupValue("databasePath", dbPath))
    {
        LOG_INFO("'databasePath' not found in the configuration file, using default: " << dbPath
                << " and adding this to the configuration file.");
           config.getRoot().add("databasePath", libconfig::Setting::TypeString)=dbPath;
    }

    WatcherdPtr theWatcherDaemon(new Watcherd(readOnly));
    try
    {
        theWatcherDaemon->run(address, port, (int)numThreads);
    }
    catch (std::exception &e)
    {
        LOG_FATAL("Caught exception in main(): " << e.what());
        std::cerr << "exception: " << e.what() << "\n";
    }

    // Save any configuration changes made during the run.
    LOG_INFO("Saving last known configuration to " << configFilename);
    SingletonConfig::lock();
    config.writeFile(configFilename.c_str());

    return 0;
}
<commit_msg>allow -d database on the command line in the watcherd. command line overrides cfg files settings. This makes it faster to switch between playing back different event database.<commit_after>/* Copyright 2009 SPARTA, Inc., dba Cobham Analytic Solutions
 *
 * This file is part of WATCHER.
 *
 *     WATCHER is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Affero General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     WATCHER is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Affero General Public License for more details.
 *
 *     You should have received a copy of the GNU Affero General Public License
 *     along with Watcher.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <iostream>
#include <string>
#include <getopt.h>

#include "server.h"
#include "logger.h"
#include "libconfig.h++"
#include "initConfig.h"
#include "singletonConfig.h"
#include "watcherd.h"

using namespace std;
using namespace watcher;
using namespace libconfig;

option Options[] = {
    { "help", 0, NULL, 'h' },
    { "config", 1, NULL, 'c' },
    { "database", 1, NULL, 'd' },
    { "read-only", 1, NULL, 'r' },
    { 0, 0, NULL, 0 }
};

void usage(const char *progName, bool exitp)
{
    cout << "Usage: " << basename(progName) << " [-c config filename]" << endl;
    cout << "Args: " << endl;
    cout << "   -h, show this messsage and exit." << endl;
    cout << "   -d database, use this event database when running watcherd" << endl;
    cout << "   -c configfile - If not given a filename of the form \""<< basename(progName) << ".cfg\" is assumed." << endl;
    cout << "   -r, --read-only - do not write events to the database." << endl;
    cout << "If a configuration file is not found on startup, a default one will be created, used, and saved on program exit." << endl;

    if (exitp)
        exit(EXIT_FAILURE);
}

DECLARE_GLOBAL_LOGGER("watcherdMain");

int main(int argc, char* argv[])
{
    TRACE_ENTER();

    int i;
    bool readOnly = false;
    std::string dbPath;
    while ((i = getopt_long(argc, argv, "hc:d:r", Options, NULL)) != -1) {
        switch (i) {
            case 'c':
                //handled below
                break;
            case 'r':
                readOnly = true;
                break;
            case 'd':
                dbPath=string(optarg);
                break;
            default:
                usage(argv[0], true);
        }
    }

    string configFilename;
    Config &config=SingletonConfig::instance();
    SingletonConfig::lock();
    if (false==initConfig(config, argc, argv, configFilename))
    {
        cerr << "Error reading configuration file, unable to continue." << endl;
        cerr << "Usage: " << basename(argv[0]) << " [-c|--configFile] configfile" << endl;
        return 1;
    }
    SingletonConfig::unlock();

    string logConf("log.properties");
    if (!config.lookupValue("logPropertiesFile", logConf))
    {
        cout << "Unable to find logPropertiesFile setting in the configuration file, using default: " << logConf
             << " and adding it to the configuration file." << endl;
        config.getRoot().add("logPropertiesFile", libconfig::Setting::TypeString)=logConf;
    }

    LOAD_LOG_PROPS(logConf);

    LOG_INFO("Logger initialized from file \"" << logConf << "\"");

    string address("glory");
    string port("8095");
    size_t numThreads=8;

    if (!config.lookupValue("server", address))
    {
        LOG_INFO("'server' not found in the configuration file, using default: " << address
                << " and adding this to the configuration file.");
        config.getRoot().add("server", libconfig::Setting::TypeString) = address;
    }

    if (!config.lookupValue("port", port))
    {
        LOG_INFO("'port' not found in the configuration file, using default: " << port
                << " and adding this to the configuration file.");
        config.getRoot().add("port", libconfig::Setting::TypeString)=port;
    }

    if (!config.lookupValue("serverThreadNum", numThreads))
    {
        LOG_INFO("'serverThreadNum' not found in the configuration file, using default: " << numThreads
                << " and adding this to the configuration file.");
           config.getRoot().add("serverThreadNum", libconfig::Setting::TypeInt)=static_cast<int>(numThreads);
    }

    std::string tmpDBPath("event.db");
    if (!config.lookupValue("databasePath", tmpDBPath))
    {
        LOG_INFO("'databasePath' not found in the configuration file, using default: " << dbPath
                << " and adding this to the configuration file.");
           config.getRoot().add("databasePath", libconfig::Setting::TypeString)=tmpDBPath;
    }
    if (dbPath.size())
        config.getRoot()["databasePath"]=dbPath;

    WatcherdPtr theWatcherDaemon(new Watcherd(readOnly));
    try
    {
        theWatcherDaemon->run(address, port, (int)numThreads);
    }
    catch (std::exception &e)
    {
        LOG_FATAL("Caught exception in main(): " << e.what());
        std::cerr << "exception: " << e.what() << "\n";
    }

    // Save any configuration changes made during the run.
    LOG_INFO("Saving last known configuration to " << configFilename);
    SingletonConfig::lock();
    config.writeFile(configFilename.c_str());

    return 0;
}
<|endoftext|>


<commit_before>///
/// @file  P2.cpp
/// @brief 2nd partial sieve function.
///
/// Copyright (C) 2014 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <primecount-internal.hpp>
#include <primesieve.hpp>
#include <aligned_vector.hpp>
#include <BitSieve.hpp>
#include <generate.hpp>
#include <pmath.hpp>
#include <ptypes.hpp>

#include <stdint.h>
#include <algorithm>
#include <iostream>
#include <vector>

#ifdef _OPENMP
  #include <omp.h>
#endif

using namespace std;
using namespace primecount;

namespace {
namespace P2 {

/// @return previous_prime or -1 if old <= 2
inline int64_t get_previous_prime(primesieve::iterator* iter, int64_t old)
{
  return (old > 2) ? iter->previous_prime() : -1;
}

/// For each prime calculate its first multiple >= low
vector<int64_t> generate_next_multiples(int64_t low, int64_t size, vector<int32_t>& primes)
{
  vector<int64_t> next;

  next.reserve(size);
  next.push_back(0);

  for (int64_t b = 1; b < size; b++)
  {
    int64_t prime = primes[b];
    int64_t next_multiple = ceil_div(low, prime) * prime;
    next_multiple += prime * (~next_multiple & 1);
    next_multiple = max(prime * prime, next_multiple);
    next.push_back(next_multiple);
  }

  return next;
}

template <typename T>
T P2_thread(T x,
            int64_t y,
            int64_t segment_size,
            int64_t segments_per_thread,
            int64_t thread_num,
            int64_t low,
            int64_t limit,
            int64_t& pix,
            int64_t& pix_count,
            vector<int32_t>& primes)
{
  pix = 0;
  pix_count = 0;
  low += thread_num * segments_per_thread * segment_size;
  limit = min(low + segments_per_thread * segment_size, limit);
  int64_t size = pi_bsearch(primes, isqrt(limit)) + 1;
  int64_t start = (int64_t) max((int64_t) (x / limit + 1), y);
  int64_t stop  = (int64_t) min(x / low, isqrt(x));
  T P2_thread = 0;

  // P2_thread = \sum_{i=pi[start]}^{pi[stop]} pi(x / primes[i]) - pi(low - 1)
  // We use a reverse prime iterator to calculate P2_thread
  primesieve::iterator iter(stop + 1, start);
  int64_t previous_prime = get_previous_prime(&iter, stop + 1);
  int64_t xp = (int64_t) (x / previous_prime);

  vector<int64_t> next = generate_next_multiples(low, size, primes);
  BitSieve sieve(segment_size);

  // segmented sieve of Eratosthenes
  for (; low < limit; low += segment_size)
  {
    // current segment = interval [low, high[
    int64_t high = min(low + segment_size, limit);
    int64_t sqrt = isqrt(high - 1);
    int64_t j = 0;

    sieve.memset(low);

    // cross-off multiples
    for (int64_t i = 2; i < size && primes[i] <= sqrt; i++)
    {
      int64_t k;
      int64_t p2 = primes[i] * 2;
      for (k = next[i]; k < high; k += p2)
        sieve.unset(k - low);
      next[i] = k;
    }

    while (previous_prime >= start && xp < high)
    {
      pix += sieve.count(j, xp - low);
      j = xp - low + 1;
      pix_count++;
      P2_thread += pix;
      previous_prime = get_previous_prime(&iter, previous_prime);
      xp = (int64_t) (x / previous_prime);
    }

    pix += sieve.count(j, (high - 1) - low);
  }

  return P2_thread;
}

/// 2nd partial sieve function.
/// P2(x, y) counts the numbers <= x that have exactly 2 prime
/// factors each exceeding the a-th prime, a = pi(y).
/// Space complexity: O((x / y)^(1/2)).
///
template <typename T>
T P2(T x, int64_t y, int threads)
{
  T a = pi_legendre(y, 1);
  T b = pi_legendre((int64_t) isqrt(x), 1);

  if (x < 4 || a >= b)
    return 0;

  if (print_status())
  {
    cout << endl;
    cout << "=== P2(x, y) ===" << endl;
    cout << "Computation of the 2nd partial sieve function" << endl;
  }

  double time = get_wtime();
  int64_t low = 2;
  int64_t limit = (int64_t)(x / max<int64_t>(1, y));
  int64_t segment_size = max<int64_t>(1 << 12, isqrt(limit));
  int64_t segments_per_thread = 1;
  threads = validate_threads(threads, limit);

  vector<int32_t> primes = generate_primes(isqrt(limit));
  aligned_vector<int64_t> pix(threads);
  aligned_vector<int64_t> pix_counts(threads);

  // \sum_{i=a+1}^{b} pi(x / primes[i]) - (i - 1)
  // initialize with \sum_{i=a+1}^{b} -i + 1
  T sum = (a - 2) * (a + 1) / 2 - (b - 2) * (b + 1) / 2;
  T pix_total = 0;

  while (low < limit)
  {
    int64_t segments = ceil_div(limit - low, segment_size);
    threads = in_between(1, threads, segments);
    segments_per_thread = in_between(1, segments_per_thread, ceil_div(segments, threads));
    double seconds = get_wtime();

    #pragma omp parallel for num_threads(threads) reduction(+: sum)
    for (int i = 0; i < threads; i++)
      sum += P2_thread(x, y, segment_size, segments_per_thread, i,
         low, limit, pix[i], pix_counts[i], primes);

    low += segments_per_thread * threads * segment_size;
    seconds = get_wtime() - seconds;

    // Add missing sum contributions in order
    for (int i = 0; i < threads; i++)
    {
      sum += pix_total * pix_counts[i];
      pix_total += pix[i];
    }

    // Adjust thread load balancing
    if (seconds < 10)
      segments_per_thread *= 2;
    else if (seconds > 30 && segments_per_thread > 1)
      segments_per_thread /= 2;

    if (print_status())
      cout << "\rStatus: " << get_percent(low, limit) << '%' << flush;
  }

  if (print_status())
    print_result("P2", sum, time);

  return sum;
}

} // namespace P2
} // namespace

namespace primecount {

int64_t P2(int64_t x, int64_t y, int threads)
{
  return P2::P2(x, y, threads);
}

#ifdef HAVE_INT128_T

int128_t P2(int128_t x, int64_t y, int threads)
{
  return P2::P2(x, y, threads);
}

#endif

/// 2nd partial sieve function.
/// P2_lehmer(x, a) counts the numbers <= x that have exactly 2 prime
/// factors each exceeding the a-th prime. This implementation is
/// optimized for small values of a < pi(x^(1/3)) which requires
/// sieving up to a large limit (x / primes[a]). Sieving is done in
/// parallel using primesieve (segmented sieve of Eratosthenes).
/// Space complexity: O(pi(sqrt(x))).
///
int64_t P2_lehmer(int64_t x, int64_t a, int threads)
{
  if (print_status())
  {
    cout << endl;
    cout << "=== P2_lehmer(x, a) ===" << endl;
    cout << "Computation of the 2nd partial sieve function" << endl;
  }

  double time = get_wtime();
  vector<int32_t> primes = generate_primes(isqrt(x));
  vector<int64_t> counts(primes.size());

  int64_t b = pi_bsearch(primes, isqrt(x));
  int64_t sum = 0;
  int64_t pix = 0;

  #pragma omp parallel for num_threads(validate_threads(threads)) schedule(dynamic)
  for (int64_t i = b; i > a; i--)
  {
    int64_t prev = (i == b) ? 0 : x / primes[i + 1] + 1;
    int64_t xi = x / primes[i];
    counts[i] = primesieve::count_primes(prev, xi);
  }

  for (int64_t i = b; i > a; i--)
  {
    pix += counts[i];
    sum += pix - (i - 1);
  }

  if (print_status())
    print_result("P2", sum, time);

  return sum;
}

} // namespace primecount
<commit_msg>Improved P2 load balancer<commit_after>///
/// @file  P2.cpp
/// @brief 2nd partial sieve function.
///
/// Copyright (C) 2014 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <primecount-internal.hpp>
#include <primesieve.hpp>
#include <aligned_vector.hpp>
#include <BitSieve.hpp>
#include <generate.hpp>
#include <pmath.hpp>
#include <ptypes.hpp>

#include <stdint.h>
#include <algorithm>
#include <iostream>
#include <vector>

#ifdef _OPENMP
  #include <omp.h>
#endif

using namespace std;
using namespace primecount;

namespace {
namespace P2 {

/// @return previous_prime or -1 if old <= 2
inline int64_t get_previous_prime(primesieve::iterator* iter, int64_t old)
{
  return (old > 2) ? iter->previous_prime() : -1;
}

/// For each prime calculate its first multiple >= low
vector<int64_t> generate_next_multiples(int64_t low, int64_t size, vector<int32_t>& primes)
{
  vector<int64_t> next;

  next.reserve(size);
  next.push_back(0);

  for (int64_t b = 1; b < size; b++)
  {
    int64_t prime = primes[b];
    int64_t next_multiple = ceil_div(low, prime) * prime;
    next_multiple += prime * (~next_multiple & 1);
    next_multiple = max(prime * prime, next_multiple);
    next.push_back(next_multiple);
  }

  return next;
}

template <typename T>
T P2_thread(T x,
            int64_t y,
            int64_t segment_size,
            int64_t segments_per_thread,
            int64_t thread_num,
            int64_t low,
            int64_t limit,
            int64_t& pix,
            int64_t& pix_count,
            vector<int32_t>& primes)
{
  pix = 0;
  pix_count = 0;
  low += thread_num * segments_per_thread * segment_size;
  limit = min(low + segments_per_thread * segment_size, limit);
  int64_t size = pi_bsearch(primes, isqrt(limit)) + 1;
  int64_t start = (int64_t) max((int64_t) (x / limit + 1), y);
  int64_t stop  = (int64_t) min(x / low, isqrt(x));
  T P2_thread = 0;

  // P2_thread = \sum_{i=pi[start]}^{pi[stop]} pi(x / primes[i]) - pi(low - 1)
  // We use a reverse prime iterator to calculate P2_thread
  primesieve::iterator iter(stop + 1, start);
  int64_t previous_prime = get_previous_prime(&iter, stop + 1);
  int64_t xp = (int64_t) (x / previous_prime);

  vector<int64_t> next = generate_next_multiples(low, size, primes);
  BitSieve sieve(segment_size);

  // segmented sieve of Eratosthenes
  for (; low < limit; low += segment_size)
  {
    // current segment = interval [low, high[
    int64_t high = min(low + segment_size, limit);
    int64_t sqrt = isqrt(high - 1);
    int64_t j = 0;

    sieve.memset(low);

    // cross-off multiples
    for (int64_t i = 2; i < size && primes[i] <= sqrt; i++)
    {
      int64_t k;
      int64_t p2 = primes[i] * 2;
      for (k = next[i]; k < high; k += p2)
        sieve.unset(k - low);
      next[i] = k;
    }

    while (previous_prime >= start && xp < high)
    {
      pix += sieve.count(j, xp - low);
      j = xp - low + 1;
      pix_count++;
      P2_thread += pix;
      previous_prime = get_previous_prime(&iter, previous_prime);
      xp = (int64_t) (x / previous_prime);
    }

    pix += sieve.count(j, (high - 1) - low);
  }

  return P2_thread;
}

void balanceLoad(int64_t* segments_per_thread, double seconds1, double time1)
{
  double time2 = get_wtime();
  double seconds = time2 - seconds1;
  double time = time2 - time1;
  double increase_threshold = in_between(0.3, time / 100, 20);

  if (seconds < increase_threshold)
    *segments_per_thread += *segments_per_thread * 3;
  else if (*segments_per_thread >= 4)
    *segments_per_thread -= *segments_per_thread / 4;
}

/// 2nd partial sieve function.
/// P2(x, y) counts the numbers <= x that have exactly 2 prime
/// factors each exceeding the a-th prime, a = pi(y).
/// Space complexity: O((x / y)^(1/2)).
///
template <typename T>
T P2(T x, int64_t y, int threads)
{
  T a = pi_legendre(y, 1);
  T b = pi_legendre((int64_t) isqrt(x), 1);

  if (x < 4 || a >= b)
    return 0;

  if (print_status())
  {
    cout << endl;
    cout << "=== P2(x, y) ===" << endl;
    cout << "Computation of the 2nd partial sieve function" << endl;
  }

  double time = get_wtime();
  int64_t low = 2;
  int64_t limit = (int64_t)(x / max<int64_t>(1, y));
  int64_t segment_size = max<int64_t>(1 << 12, isqrt(limit));
  int64_t segments_per_thread = 1;
  threads = validate_threads(threads, limit);

  vector<int32_t> primes = generate_primes(isqrt(limit));
  aligned_vector<int64_t> pix(threads);
  aligned_vector<int64_t> pix_counts(threads);

  // \sum_{i=a+1}^{b} pi(x / primes[i]) - (i - 1)
  // initialize with \sum_{i=a+1}^{b} -i + 1
  T sum = (a - 2) * (a + 1) / 2 - (b - 2) * (b + 1) / 2;
  T pix_total = 0;

  while (low < limit)
  {
    int64_t segments = ceil_div(limit - low, segment_size);
    threads = in_between(1, threads, segments);
    segments_per_thread = in_between(1, segments_per_thread, ceil_div(segments, threads));
    double seconds = get_wtime();

    #pragma omp parallel for num_threads(threads) reduction(+: sum)
    for (int i = 0; i < threads; i++)
      sum += P2_thread(x, y, segment_size, segments_per_thread, i,
         low, limit, pix[i], pix_counts[i], primes);

    low += segments_per_thread * threads * segment_size;
    balanceLoad(&segments_per_thread, seconds, time);

    // Add missing sum contributions in order
    for (int i = 0; i < threads; i++)
    {
      sum += pix_total * pix_counts[i];
      pix_total += pix[i];
    }

    if (print_status())
      cout << "\rStatus: " << get_percent(low, limit) << '%' << flush;
  }

  if (print_status())
    print_result("P2", sum, time);

  return sum;
}

} // namespace P2
} // namespace

namespace primecount {

int64_t P2(int64_t x, int64_t y, int threads)
{
  return P2::P2(x, y, threads);
}

#ifdef HAVE_INT128_T

int128_t P2(int128_t x, int64_t y, int threads)
{
  return P2::P2(x, y, threads);
}

#endif

/// 2nd partial sieve function.
/// P2_lehmer(x, a) counts the numbers <= x that have exactly 2 prime
/// factors each exceeding the a-th prime. This implementation is
/// optimized for small values of a < pi(x^(1/3)) which requires
/// sieving up to a large limit (x / primes[a]). Sieving is done in
/// parallel using primesieve (segmented sieve of Eratosthenes).
/// Space complexity: O(pi(sqrt(x))).
///
int64_t P2_lehmer(int64_t x, int64_t a, int threads)
{
  if (print_status())
  {
    cout << endl;
    cout << "=== P2_lehmer(x, a) ===" << endl;
    cout << "Computation of the 2nd partial sieve function" << endl;
  }

  double time = get_wtime();
  vector<int32_t> primes = generate_primes(isqrt(x));
  vector<int64_t> counts(primes.size());

  int64_t b = pi_bsearch(primes, isqrt(x));
  int64_t sum = 0;
  int64_t pix = 0;

  #pragma omp parallel for num_threads(validate_threads(threads)) schedule(dynamic)
  for (int64_t i = b; i > a; i--)
  {
    int64_t prev = (i == b) ? 0 : x / primes[i + 1] + 1;
    int64_t xi = x / primes[i];
    counts[i] = primesieve::count_primes(prev, xi);
  }

  for (int64_t i = b; i > a; i--)
  {
    pix += counts[i];
    sum += pix - (i - 1);
  }

  if (print_status())
    print_result("P2", sum, time);

  return sum;
}

} // namespace primecount
<|endoftext|>


<commit_before>/********************************
** Tsunagari Tile Engine       **
** file-type.cpp               **
** Copyright 2016 Paul Merrill **
********************************/

// **********
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
// **********

#include "pack/file-type.h"

#include <string>

static const std::string textExtensions[] = {
        ".json"
};

static const std::string mediaExtensions[] = {
        ".oga",
        ".png"
};

FileType determineFileType(const std::string& path) {
    auto dot = path.rfind('.');
    std::string extension = path.substr(dot);
    for (auto& textExtension : textExtensions) {
        if (extension == textExtension) {
            return FT_TEXT;
        }
    }
    for (auto& mediaExtension : mediaExtensions) {
        if (extension == mediaExtension) {
            return FT_MEDIA;
        }
    }
    return FT_UNKNOWN;
}
<commit_msg>pack/file-type: Fix out-of-bounds error<commit_after>/********************************
** Tsunagari Tile Engine       **
** file-type.cpp               **
** Copyright 2016 Paul Merrill **
********************************/

// **********
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
// **********

#include "pack/file-type.h"

#include <string>

static const std::string textExtensions[] = {
        ".json"
};

static const std::string mediaExtensions[] = {
        ".oga",
        ".png"
};

FileType determineFileType(const std::string& path) {
    auto dot = path.rfind('.');
    if (dot == -1) {
        return FT_UNKNOWN;
    }
    std::string extension = path.substr(dot);
    for (auto& textExtension : textExtensions) {
        if (extension == textExtension) {
            return FT_TEXT;
        }
    }
    for (auto& mediaExtension : mediaExtensions) {
        if (extension == mediaExtension) {
            return FT_MEDIA;
        }
    }
    return FT_UNKNOWN;
}
<|endoftext|>


<commit_before>///
/// @file  P2.cpp
/// @brief 2nd partial sieve function.
///
/// Copyright (C) 2014 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <primecount-internal.hpp>
#include <primesieve.hpp>
#include <pi_bsearch.hpp>
#include <pmath.hpp>
#include <utils.hpp>

#include <stdint.h>
#include <algorithm>
#include <vector>

#ifdef _OPENMP
  #include <omp.h>
#endif

using namespace std;

namespace {

int64_t P2_thread(int64_t x,
                  int64_t y,
                  int64_t segment_size,
                  int64_t segments_per_thread,
                  int64_t thread_num,
                  int64_t low,
                  int64_t limit,
                  int64_t& pix,
                  int64_t& pix_count,
                  vector<int32_t>& primes)
{
  pix = 0;
  pix_count = 0;
  low += thread_num * segments_per_thread * segment_size;
  limit = min(low + segments_per_thread * segment_size, limit);
  int64_t size = pi_bsearch(primes, isqrt(limit)) + 1;
  int64_t P2_thread = 0;
  int64_t start = max(x / limit + 1, y);
  int64_t stop = in_between(2, x / low, isqrt(x));

  vector<char> sieve(segment_size);
  vector<int64_t> pi_input;
  vector<int64_t> next;
  next.push_back(0);
  next.reserve(size);

  // P2_thread = \sum_{i=pi[start]}^{pi[stop]} pi(x / primes[i])
  primesieve::generate_primes(start, stop, &pi_input);

  // reverse iterator
  auto iter = pi_input.rbegin();
  auto rend = pi_input.rend();

  for (size_t i = 0; i < pi_input.size(); i++)
    pi_input[i] = x / pi_input[i];

  // initialize next multiples
  for (int64_t b = 1; b < size; b++)
  {
    int64_t prime = primes[b];
    int64_t next_multiple = ((low + prime - 1) / prime) * prime;
    next_multiple = max(isquare(prime), next_multiple + (~next_multiple & 1) * prime);
    next.push_back(next_multiple);
  }

  // segmented sieve of Eratosthenes
  for (; low < limit; low += segment_size)
  {
    fill(sieve.begin(), sieve.end(), 1);

    // current segment = interval [low, high[
    int64_t high = min(low + segment_size, limit);
    int64_t sqrt = isqrt(high - 1);
    int64_t j = ~low & 1;

    // cross-off multiples
    for (int64_t i = 2; i < size && primes[i] <= sqrt; i++)
    {
      int64_t k;
      int64_t p2 = primes[i] * 2;
      for (k = next[i]; k < high; k += p2)
        sieve[k - low] = 0;
      next[i] = k;
    }

    for (; iter != rend && *iter < high; iter++)
    {
      for (int64_t xil = *iter - low; j <= xil; j += 2)
        pix += sieve[j];
      // P2_thread += pi(x / primes[i])
      P2_thread += pix;
      pix_count++;
    }

    for (; j < high - low; j += 2)
      pix += sieve[j];
  }

  return P2_thread;
}

} // namespace

namespace primecount {

/// 2nd partial sieve function.
/// P2(x, y) counts the numbers <= x that have exactly 2 prime
/// factors each exceeding the a-th prime, a = pi(y).
/// Space complexity: O((x / y)^(1/2)).
///
int64_t P2(int64_t x, int64_t y, int threads)
{
  int64_t a = pi_legendre(y, 1);
  int64_t b = pi_legendre(isqrt(x), 1);

  if (x < 4 || a >= b)
    return 0;

  threads = validate_threads(threads);

  // \sum_{i=a+1}^{b} pi(x / primes[i]) - (i - 1)
  // initialize with \sum_{i=a+1}^{b} -i + 1
  int64_t sum = (a - 2) * (a + 1) / 2 - (b - 2) * (b + 1) / 2;
  int64_t pix_total = 1;
  int64_t low = 3;
  int64_t limit = (y > 0) ? x / y : x;
  int64_t sqrt_limit = isqrt(limit);
  int64_t min_segment_size = 64;
  int64_t segment_size = max(min_segment_size, sqrt_limit);
  int64_t segments_per_thread = 1;

  vector<int32_t> primes;
  primes.push_back(0);
  primesieve::generate_primes(sqrt_limit, &primes);
  vector<int64_t> pix_counts(threads);
  vector<int64_t> pix(threads);

  while (low < limit)
  {
    int64_t segments = (limit - low + segment_size - 1) / segment_size;
    threads = in_between(1, threads, segments);
    segments_per_thread = in_between(1, segments_per_thread, (segments + threads - 1) / threads);
    double seconds = get_wtime();

    #pragma omp parallel for num_threads(threads) reduction(+: sum)
    for (int i = 0; i < threads; i++)
      sum += P2_thread(x, y, segment_size, segments_per_thread, i, low, limit,
          pix[i], pix_counts[i], primes);

    seconds = get_wtime() - seconds;
    low += segments_per_thread * threads * segment_size;

    // Adjust thread load balancing
    if (seconds < 10)
      segments_per_thread *= 2;
    else if (seconds > 30 && segments_per_thread > 1)
      segments_per_thread /= 2;

    // Add the missing sum contributions in order
    for (int i = 0; i < threads; i++)
    {
      sum += pix_total * pix_counts[i];
      pix_total += pix[i];
    }
  }

  return sum;
}

/// 2nd partial sieve function.
/// P2_lehmer(x, a) counts the numbers <= x that have exactly 2 prime
/// factors each exceeding the a-th prime. This implementation is
/// optimized for small values of a < pi(x^(1/3)) which requires
/// sieving up to a large limit (x / primes[a]). Sieving is done in
/// parallel using primesieve (segmented sieve of Eratosthenes).
/// Space complexity: O(pi(sqrt(x))).
///
int64_t P2_lehmer(int64_t x, int64_t a, int threads)
{
  vector<int32_t> primes;
  vector<int64_t> counts;
  primes.push_back(0);
  primesieve::generate_primes(isqrt(x), &primes);
  counts.resize(primes.size());

  int64_t b = pi_bsearch(primes, isqrt(x));
  int64_t sum = 0;
  int64_t pix = 0;

  #pragma omp parallel for num_threads(validate_threads(threads)) schedule(dynamic)
  for (int64_t i = b; i > a; i--)
  {
    int64_t prev = (i == b) ? 0 : x / primes[i + 1] + 1;
    int64_t xi = x / primes[i];
    counts[i] = primesieve::count_primes(prev, xi);
  }

  for (int64_t i = b; i > a; i--)
  {
    pix += counts[i];
    sum += pix - (i - 1);
  }

  return sum;
}

} // namespace primecount
<commit_msg>Refactoring<commit_after>///
/// @file  P2.cpp
/// @brief 2nd partial sieve function.
///
/// Copyright (C) 2014 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <primecount-internal.hpp>
#include <primesieve.hpp>
#include <pi_bsearch.hpp>
#include <pmath.hpp>
#include <utils.hpp>

#include <stdint.h>
#include <algorithm>
#include <vector>

#ifdef _OPENMP
  #include <omp.h>
#endif

using namespace std;

namespace {

int64_t P2_thread(int64_t x,
                  int64_t y,
                  int64_t segment_size,
                  int64_t segments_per_thread,
                  int64_t thread_num,
                  int64_t low,
                  int64_t limit,
                  int64_t& pix,
                  int64_t& pix_count,
                  vector<int32_t>& primes)
{
  pix = 0;
  pix_count = 0;
  low += thread_num * segments_per_thread * segment_size;
  limit = min(low + segments_per_thread * segment_size, limit);
  int64_t size = pi_bsearch(primes, isqrt(limit)) + 1;
  int64_t P2_thread = 0;
  int64_t start = max(x / limit + 1, y);
  int64_t stop = in_between(2, x / low, isqrt(x));

  vector<char> sieve(segment_size);
  vector<int64_t> pi_input;
  vector<int64_t> next;
  next.push_back(0);
  next.reserve(size);

  // P2_thread = \sum_{i=pi[start]}^{pi[stop]} pi(x / primes[i])
  primesieve::generate_primes(start, stop, &pi_input);

  // reverse iterator
  auto iter = pi_input.rbegin();
  auto rend = pi_input.rend();

  for (size_t i = 0; i < pi_input.size(); i++)
    pi_input[i] = x / pi_input[i];

  // initialize next multiples
  for (int64_t b = 1; b < size; b++)
  {
    int64_t prime = primes[b];
    int64_t next_multiple = ((low + prime - 1) / prime) * prime;
    next_multiple = max(isquare(prime), next_multiple + (~next_multiple & 1) * prime);
    next.push_back(next_multiple);
  }

  // segmented sieve of Eratosthenes
  for (; low < limit; low += segment_size)
  {
    fill(sieve.begin(), sieve.end(), 1);

    // current segment = interval [low, high[
    int64_t high = min(low + segment_size, limit);
    int64_t sqrt = isqrt(high - 1);
    int64_t j = ~low & 1;

    // cross-off multiples
    for (int64_t i = 2; i < size && primes[i] <= sqrt; i++)
    {
      int64_t k;
      int64_t p2 = primes[i] * 2;
      for (k = next[i]; k < high; k += p2)
        sieve[k - low] = 0;
      next[i] = k;
    }

    for (; iter != rend && *iter < high; iter++)
    {
      for (int64_t xi = *iter - low; j <= xi; j += 2)
        pix += sieve[j];
      // P2_thread += pi(x / primes[i])
      P2_thread += pix;
      pix_count++;
    }

    for (; j < high - low; j += 2)
      pix += sieve[j];
  }

  return P2_thread;
}

} // namespace

namespace primecount {

/// 2nd partial sieve function.
/// P2(x, y) counts the numbers <= x that have exactly 2 prime
/// factors each exceeding the a-th prime, a = pi(y).
/// Space complexity: O((x / y)^(1/2)).
///
int64_t P2(int64_t x, int64_t y, int threads)
{
  int64_t a = pi_legendre(y, 1);
  int64_t b = pi_legendre(isqrt(x), 1);

  if (x < 4 || a >= b)
    return 0;

  threads = validate_threads(threads);

  // \sum_{i=a+1}^{b} pi(x / primes[i]) - (i - 1)
  // initialize with \sum_{i=a+1}^{b} -i + 1
  int64_t sum = (a - 2) * (a + 1) / 2 - (b - 2) * (b + 1) / 2;
  int64_t pix_total = 1;
  int64_t low = 3;
  int64_t limit = (y > 0) ? x / y : x;
  int64_t sqrt_limit = isqrt(limit);
  int64_t min_segment_size = 64;
  int64_t segment_size = max(min_segment_size, sqrt_limit);
  int64_t segments_per_thread = 1;

  vector<int32_t> primes;
  primes.push_back(0);
  primesieve::generate_primes(sqrt_limit, &primes);
  vector<int64_t> pix_counts(threads);
  vector<int64_t> pix(threads);

  while (low < limit)
  {
    int64_t segments = (limit - low + segment_size - 1) / segment_size;
    threads = in_between(1, threads, segments);
    segments_per_thread = in_between(1, segments_per_thread, (segments + threads - 1) / threads);
    double seconds = get_wtime();

    #pragma omp parallel for num_threads(threads) reduction(+: sum)
    for (int i = 0; i < threads; i++)
      sum += P2_thread(x, y, segment_size, segments_per_thread, i, low, limit,
          pix[i], pix_counts[i], primes);

    seconds = get_wtime() - seconds;
    low += segments_per_thread * threads * segment_size;

    // Adjust thread load balancing
    if (seconds < 10)
      segments_per_thread *= 2;
    else if (seconds > 30 && segments_per_thread > 1)
      segments_per_thread /= 2;

    // Add the missing sum contributions in order
    for (int i = 0; i < threads; i++)
    {
      sum += pix_total * pix_counts[i];
      pix_total += pix[i];
    }
  }

  return sum;
}

/// 2nd partial sieve function.
/// P2_lehmer(x, a) counts the numbers <= x that have exactly 2 prime
/// factors each exceeding the a-th prime. This implementation is
/// optimized for small values of a < pi(x^(1/3)) which requires
/// sieving up to a large limit (x / primes[a]). Sieving is done in
/// parallel using primesieve (segmented sieve of Eratosthenes).
/// Space complexity: O(pi(sqrt(x))).
///
int64_t P2_lehmer(int64_t x, int64_t a, int threads)
{
  vector<int32_t> primes;
  vector<int64_t> counts;
  primes.push_back(0);
  primesieve::generate_primes(isqrt(x), &primes);
  counts.resize(primes.size());

  int64_t b = pi_bsearch(primes, isqrt(x));
  int64_t sum = 0;
  int64_t pix = 0;

  #pragma omp parallel for num_threads(validate_threads(threads)) schedule(dynamic)
  for (int64_t i = b; i > a; i--)
  {
    int64_t prev = (i == b) ? 0 : x / primes[i + 1] + 1;
    int64_t xi = x / primes[i];
    counts[i] = primesieve::count_primes(prev, xi);
  }

  for (int64_t i = b; i > a; i--)
  {
    pix += counts[i];
    sum += pix - (i - 1);
  }

  return sum;
}

} // namespace primecount
<|endoftext|>


<commit_before>#include "archivewidget.h"

WARNINGS_DISABLE
#include <QAction>
#include <QComboBox>
#include <QCursor>
#include <QDateTime>
#include <QEvent>
#include <QKeyEvent>
#include <QKeySequence>
#include <QLabel>
#include <QMenu>
#include <QModelIndex>
#include <QModelIndexList>
#include <QSortFilterProxyModel>
#include <QStringList>
#include <QTableView>
#include <QVariant>
#include <Qt>

#include "ui_archivewidget.h"
WARNINGS_ENABLE

#include "TElidedLabel.h"

#include "messages/archiverestoreoptions.h"

#include "basetask.h"
#include "elidedclickablelabel.h"
#include "filetablemodel.h"
#include "persistentmodel/archive.h"
#include "restoredialog.h"
#include "utils.h"

#define EMPTY_TAR_ARCHIVE_BYTES 2000

ArchiveDetailsWidget::ArchiveDetailsWidget(QWidget *parent)
    : QWidget(parent),
      _ui(new Ui::ArchiveDetailsWidget),
      _contentsModel(new FileTableModel(this)),
      _proxyModel(new QSortFilterProxyModel(_contentsModel)),
      _fileMenu(new QMenu(this))
{
    _ui->setupUi(this);
    updateUi();

    // Set up filter UI.
    _ui->filterComboBox->hide();
    _proxyModel->setDynamicSortFilter(false);
    _proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
    _proxyModel->setSourceModel(_contentsModel);
    _ui->archiveContentsTableView->setModel(_proxyModel);
    _ui->archiveContentsTableView->setContextMenuPolicy(Qt::CustomContextMenu);

    // Set up other UI.
    _fileMenu->addAction(_ui->actionRestoreFiles);

    // Connections for UI functionality.
    connect(_ui->archiveContentsTableView,
            &QTableView::customContextMenuRequested, this,
            &ArchiveDetailsWidget::showContextMenu);
    connect(_ui->actionRestoreFiles, &QAction::triggered, this,
            &ArchiveDetailsWidget::restoreFiles);
    connect(_ui->archiveContentsTableView, &QTableView::activated, this,
            &ArchiveDetailsWidget::restoreFiles);
    connect(_ui->hideButton, &QPushButton::clicked, this,
            &ArchiveDetailsWidget::close);
    connect(_contentsModel, &FileTableModel::taskRequested, this,
            &ArchiveDetailsWidget::taskRequested);
    connect(_ui->archiveJobLabel, &ElidedClickableLabel::clicked,
            [this]() { emit jobClicked(_archive->jobRef()); });

    // Connection to reset the model.
    connect(_contentsModel, &FileTableModel::modelReset, [this]() {
        _ui->archiveContentsTableView->resizeColumnsToContents();
        _ui->archiveContentsLabel->setText(
            tr("Contents (%1)").arg(_contentsModel->rowCount()));
    });

    // Connections for filtering
    connect(_ui->filterComboBox, &QComboBox::editTextChanged, _proxyModel,
            &QSortFilterProxyModel::setFilterWildcard);
    connect(_ui->filterComboBox,
            static_cast<void (QComboBox::*)(int)>(
                &QComboBox::currentIndexChanged),
            [this]() { _ui->archiveContentsTableView->setFocus(); });
    connect(_ui->filterButton, &QPushButton::toggled,
            [this](const bool checked) {
                _ui->filterComboBox->setVisible(checked);
                if(checked)
                    _ui->filterComboBox->setFocus();
                else
                    _ui->filterComboBox->clearEditText();
            });
}

ArchiveDetailsWidget::~ArchiveDetailsWidget()
{
    delete _proxyModel;
    delete _contentsModel;
    delete _ui;
}

void ArchiveDetailsWidget::setArchive(ArchivePtr archive)
{
    // Remove previous connections (if applicable).
    if(_archive)
    {
        disconnect(_archive.data(), &Archive::changed, this,
                   &ArchiveDetailsWidget::updateDetails);
        disconnect(_archive.data(), &Archive::purged, this,
                   &ArchiveDetailsWidget::close);
    }

    // Store pointer.
    _archive = archive;

    // Set up new connections and refresh the display.
    if(_archive)
    {
        connect(_archive.data(), &Archive::changed, this,
                &ArchiveDetailsWidget::updateDetails);
        connect(_archive.data(), &Archive::purged, this,
                &ArchiveDetailsWidget::close);
        updateDetails();
    }
    else
    {
        // No need to store info about the previous Archive.
        _contentsModel->reset();
    }
}

void ArchiveDetailsWidget::updateDetails()
{
    // Bail (if applicable).
    if(!_archive)
        return;

    {
        // Show basic archive info.
        _ui->archiveNameLabel->setText(_archive->name());
        _ui->archiveDateLabel->setText(
            _archive->timestamp().toString(Qt::DefaultLocaleLongDate));

        // Show info about a linked Job (if applicable).
        if(_archive->jobRef().isEmpty())
        {
            _ui->archiveJobLabel->hide();
            _ui->archiveJobLabelField->hide();
            _ui->archiveIconLabel->setStyleSheet(
                "image: url(:/logos/tarsnap-icon-big.png)");
        }
        else
        {
            _ui->archiveJobLabel->show();
            _ui->archiveJobLabelField->show();
            _ui->archiveJobLabel->setText(_archive->jobRef());
            _ui->archiveIconLabel->setStyleSheet(
                "image: url(:/icons/hard-drive-big.png)");
        }

        // Show size info about the archive.
        _ui->archiveSizeLabel->setText(
            Utils::humanBytes(_archive->sizeTotal()));
        _ui->archiveSizeLabel->setToolTip(_archive->archiveStats());
        _ui->archiveUniqueDataLabel->setText(
            Utils::humanBytes(_archive->sizeUniqueCompressed()));
        _ui->archiveUniqueDataLabel->setToolTip(_archive->archiveStats());

        // Show other info about the archive.
        _ui->archiveCommandLineEdit->setText(_archive->command());
        _ui->archiveCommandLineEdit->setToolTip(
            _archive->command().prepend("
").append("
"));
        _ui->archiveCommandLineEdit->setCursorPosition(0);

        // Warn about partial archives.
        if(_archive->truncated())
        {
            _ui->infoLabel->setText(tr("This archive is truncated,"
                                       " data may be incomplete"));
            _ui->infoLabel->setToolTip(_archive->truncatedInfo());
            _ui->infoLabel->show();
        }
        else if(_archive->contents().isEmpty()
                && (_archive->sizeTotal() < EMPTY_TAR_ARCHIVE_BYTES))
        {
            // Warn about a potentially empty archive.
            _ui->infoLabel->setText(tr("This archive looks empty,"
                                       " no file data may be contained"
                                       " besides the TAR header"));
            _ui->infoLabel->show();
        }
        else
            _ui->infoLabel->hide();

        // Show files in the archive.
        _contentsModel->setArchive(_archive);
    }
}

void ArchiveDetailsWidget::closeEvent(QCloseEvent *event)
{
    Q_UNUSED(event)
    setArchive(ArchivePtr()); // Release memory held by the contents widget
    emit hidden();
}

void ArchiveDetailsWidget::keyPressEvent(QKeyEvent *event)
{
    // Special handling for Escape with filtering.
    if((event->key() == Qt::Key_Escape) && _ui->filterComboBox->isVisible())
    {
        if(_ui->filterComboBox->currentText().isEmpty())
        {
            _ui->filterButton->toggle();
        }
        else
        {
            _ui->filterComboBox->clearEditText();
            _ui->filterComboBox->setFocus();
        }
    }
    else
    {
        // Normal handling.
        QWidget::keyPressEvent(event);
    }
}

void ArchiveDetailsWidget::changeEvent(QEvent *event)
{
    if(event->type() == QEvent::LanguageChange)
    {
        _ui->retranslateUi(this);
        updateUi();
        updateDetails();
    }
    QWidget::changeEvent(event);
}

void ArchiveDetailsWidget::showContextMenu()
{
    _fileMenu->popup(QCursor::pos());
}

void ArchiveDetailsWidget::restoreFiles()
{
    // Get selected items, and bail if there's none.
    QModelIndexList indexes =
        _ui->archiveContentsTableView->selectionModel()->selectedRows();
    if(indexes.isEmpty())
        return;

    // Convert items to filenames.
    QStringList files;
    for(const QModelIndex &index : indexes)
        files << index.data().toString();

    // Launch RestoreDialog.
    RestoreDialog *restoreDialog = new RestoreDialog(this, _archive, files);
    restoreDialog->displayTarOption(false);
    restoreDialog->show();
    connect(restoreDialog, &RestoreDialog::accepted, [this, restoreDialog] {
        emit restoreArchive(restoreDialog->archive(),
                            restoreDialog->getOptions());
    });
}

void ArchiveDetailsWidget::updateUi()
{
    _ui->hideButton->setToolTip(_ui->hideButton->toolTip().arg(
        QKeySequence(Qt::Key_Escape).toString(QKeySequence::NativeText)));
    _ui->filterButton->setToolTip(_ui->filterButton->toolTip().arg(
        _ui->filterButton->shortcut().toString(QKeySequence::NativeText)));
}
<commit_msg>ArchiveDetailsWidget: adjust for early bail<commit_after>#include "archivewidget.h"

WARNINGS_DISABLE
#include <QAction>
#include <QComboBox>
#include <QCursor>
#include <QDateTime>
#include <QEvent>
#include <QKeyEvent>
#include <QKeySequence>
#include <QLabel>
#include <QMenu>
#include <QModelIndex>
#include <QModelIndexList>
#include <QSortFilterProxyModel>
#include <QStringList>
#include <QTableView>
#include <QVariant>
#include <Qt>

#include "ui_archivewidget.h"
WARNINGS_ENABLE

#include "TElidedLabel.h"

#include "messages/archiverestoreoptions.h"

#include "basetask.h"
#include "elidedclickablelabel.h"
#include "filetablemodel.h"
#include "persistentmodel/archive.h"
#include "restoredialog.h"
#include "utils.h"

#define EMPTY_TAR_ARCHIVE_BYTES 2000

ArchiveDetailsWidget::ArchiveDetailsWidget(QWidget *parent)
    : QWidget(parent),
      _ui(new Ui::ArchiveDetailsWidget),
      _contentsModel(new FileTableModel(this)),
      _proxyModel(new QSortFilterProxyModel(_contentsModel)),
      _fileMenu(new QMenu(this))
{
    _ui->setupUi(this);
    updateUi();

    // Set up filter UI.
    _ui->filterComboBox->hide();
    _proxyModel->setDynamicSortFilter(false);
    _proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
    _proxyModel->setSourceModel(_contentsModel);
    _ui->archiveContentsTableView->setModel(_proxyModel);
    _ui->archiveContentsTableView->setContextMenuPolicy(Qt::CustomContextMenu);

    // Set up other UI.
    _fileMenu->addAction(_ui->actionRestoreFiles);

    // Connections for UI functionality.
    connect(_ui->archiveContentsTableView,
            &QTableView::customContextMenuRequested, this,
            &ArchiveDetailsWidget::showContextMenu);
    connect(_ui->actionRestoreFiles, &QAction::triggered, this,
            &ArchiveDetailsWidget::restoreFiles);
    connect(_ui->archiveContentsTableView, &QTableView::activated, this,
            &ArchiveDetailsWidget::restoreFiles);
    connect(_ui->hideButton, &QPushButton::clicked, this,
            &ArchiveDetailsWidget::close);
    connect(_contentsModel, &FileTableModel::taskRequested, this,
            &ArchiveDetailsWidget::taskRequested);
    connect(_ui->archiveJobLabel, &ElidedClickableLabel::clicked,
            [this]() { emit jobClicked(_archive->jobRef()); });

    // Connection to reset the model.
    connect(_contentsModel, &FileTableModel::modelReset, [this]() {
        _ui->archiveContentsTableView->resizeColumnsToContents();
        _ui->archiveContentsLabel->setText(
            tr("Contents (%1)").arg(_contentsModel->rowCount()));
    });

    // Connections for filtering
    connect(_ui->filterComboBox, &QComboBox::editTextChanged, _proxyModel,
            &QSortFilterProxyModel::setFilterWildcard);
    connect(_ui->filterComboBox,
            static_cast<void (QComboBox::*)(int)>(
                &QComboBox::currentIndexChanged),
            [this]() { _ui->archiveContentsTableView->setFocus(); });
    connect(_ui->filterButton, &QPushButton::toggled,
            [this](const bool checked) {
                _ui->filterComboBox->setVisible(checked);
                if(checked)
                    _ui->filterComboBox->setFocus();
                else
                    _ui->filterComboBox->clearEditText();
            });
}

ArchiveDetailsWidget::~ArchiveDetailsWidget()
{
    delete _proxyModel;
    delete _contentsModel;
    delete _ui;
}

void ArchiveDetailsWidget::setArchive(ArchivePtr archive)
{
    // Remove previous connections (if applicable).
    if(_archive)
    {
        disconnect(_archive.data(), &Archive::changed, this,
                   &ArchiveDetailsWidget::updateDetails);
        disconnect(_archive.data(), &Archive::purged, this,
                   &ArchiveDetailsWidget::close);
    }

    // Store pointer.
    _archive = archive;

    // Set up new connections and refresh the display.
    if(_archive)
    {
        connect(_archive.data(), &Archive::changed, this,
                &ArchiveDetailsWidget::updateDetails);
        connect(_archive.data(), &Archive::purged, this,
                &ArchiveDetailsWidget::close);
        updateDetails();
    }
    else
    {
        // No need to store info about the previous Archive.
        _contentsModel->reset();
    }
}

void ArchiveDetailsWidget::updateDetails()
{
    // Bail (if applicable).
    if(!_archive)
        return;

    // Show basic archive info.
    _ui->archiveNameLabel->setText(_archive->name());
    _ui->archiveDateLabel->setText(
        _archive->timestamp().toString(Qt::DefaultLocaleLongDate));

    // Show info about a linked Job (if applicable).
    if(_archive->jobRef().isEmpty())
    {
        _ui->archiveJobLabel->hide();
        _ui->archiveJobLabelField->hide();
        _ui->archiveIconLabel->setStyleSheet(
            "image: url(:/logos/tarsnap-icon-big.png)");
    }
    else
    {
        _ui->archiveJobLabel->show();
        _ui->archiveJobLabelField->show();
        _ui->archiveJobLabel->setText(_archive->jobRef());
        _ui->archiveIconLabel->setStyleSheet(
            "image: url(:/icons/hard-drive-big.png)");
    }

    // Show size info about the archive.
    _ui->archiveSizeLabel->setText(Utils::humanBytes(_archive->sizeTotal()));
    _ui->archiveSizeLabel->setToolTip(_archive->archiveStats());
    _ui->archiveUniqueDataLabel->setText(
        Utils::humanBytes(_archive->sizeUniqueCompressed()));
    _ui->archiveUniqueDataLabel->setToolTip(_archive->archiveStats());

    // Show other info about the archive.
    _ui->archiveCommandLineEdit->setText(_archive->command());
    _ui->archiveCommandLineEdit->setToolTip(
        _archive->command().prepend("
").append("
"));
    _ui->archiveCommandLineEdit->setCursorPosition(0);

    // Warn about partial archives.
    if(_archive->truncated())
    {
        _ui->infoLabel->setText(tr("This archive is truncated,"
                                   " data may be incomplete"));
        _ui->infoLabel->setToolTip(_archive->truncatedInfo());
        _ui->infoLabel->show();
    }
    else if(_archive->contents().isEmpty()
            && (_archive->sizeTotal() < EMPTY_TAR_ARCHIVE_BYTES))
    {
        // Warn about a potentially empty archive.
        _ui->infoLabel->setText(tr("This archive looks empty,"
                                   " no file data may be contained"
                                   " besides the TAR header"));
        _ui->infoLabel->show();
    }
    else
        _ui->infoLabel->hide();

    // Show files in the archive.
    _contentsModel->setArchive(_archive);
}

void ArchiveDetailsWidget::closeEvent(QCloseEvent *event)
{
    Q_UNUSED(event)
    setArchive(ArchivePtr()); // Release memory held by the contents widget
    emit hidden();
}

void ArchiveDetailsWidget::keyPressEvent(QKeyEvent *event)
{
    // Special handling for Escape with filtering.
    if((event->key() == Qt::Key_Escape) && _ui->filterComboBox->isVisible())
    {
        if(_ui->filterComboBox->currentText().isEmpty())
        {
            _ui->filterButton->toggle();
        }
        else
        {
            _ui->filterComboBox->clearEditText();
            _ui->filterComboBox->setFocus();
        }
    }
    else
    {
        // Normal handling.
        QWidget::keyPressEvent(event);
    }
}

void ArchiveDetailsWidget::changeEvent(QEvent *event)
{
    if(event->type() == QEvent::LanguageChange)
    {
        _ui->retranslateUi(this);
        updateUi();
        updateDetails();
    }
    QWidget::changeEvent(event);
}

void ArchiveDetailsWidget::showContextMenu()
{
    _fileMenu->popup(QCursor::pos());
}

void ArchiveDetailsWidget::restoreFiles()
{
    // Get selected items, and bail if there's none.
    QModelIndexList indexes =
        _ui->archiveContentsTableView->selectionModel()->selectedRows();
    if(indexes.isEmpty())
        return;

    // Convert items to filenames.
    QStringList files;
    for(const QModelIndex &index : indexes)
        files << index.data().toString();

    // Launch RestoreDialog.
    RestoreDialog *restoreDialog = new RestoreDialog(this, _archive, files);
    restoreDialog->displayTarOption(false);
    restoreDialog->show();
    connect(restoreDialog, &RestoreDialog::accepted, [this, restoreDialog] {
        emit restoreArchive(restoreDialog->archive(),
                            restoreDialog->getOptions());
    });
}

void ArchiveDetailsWidget::updateUi()
{
    _ui->hideButton->setToolTip(_ui->hideButton->toolTip().arg(
        QKeySequence(Qt::Key_Escape).toString(QKeySequence::NativeText)));
    _ui->filterButton->setToolTip(_ui->filterButton->toolTip().arg(
        _ui->filterButton->shortcut().toString(QKeySequence::NativeText)));
}
<|endoftext|>