<commit_before>/**
 * Copyright (c) 2016-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

#include "RenameClasses.h"

#include <algorithm>
#include <map>
#include <string>
#include <vector>
#include <unordered_set>
#include <unordered_map>

#include "walkers.h"
#include "DexClass.h"
#include "DexInstruction.h"
#include "DexUtil.h"
#include "ReachableClasses.h"

#define MAX_DESCRIPTOR_LENGTH (1024)
#define MAX_IDENT_CHAR (52)
#define MAX_IDENT (MAX_IDENT_CHAR * MAX_IDENT_CHAR * MAX_IDENT_CHAR)

int match_short = 0;
int match_long = 0;
int match_inner = 0;

int base_strings_size = 0;
int ren_strings_size = 0;

static char getident(int num) {
  if (num < 26) {
    return 'a' + num;
  } else {
    return 'A' + num - 26;
  }
}

void get_next_ident(char *out, int &num) {
  // *sigh* re-write when not tired.
  int low = num;
  int mid = (num / 52);
  int top = (mid / 52);
  always_assert_log(num <= MAX_IDENT,
                    "Bailing, Ident %d, greater than maximum\n", num);
  if (top) {
    *out++ = getident(top - 1);
    low -= (top *52*52);
  }
  if (mid) {
    mid -= (top * 52);
    *out++ = getident(mid);
    low -= (mid * 52);
  }
  *out++ = getident(low);
  *out++ = '\0';
  num++;
}

void unpackage_private(Scope &scope) {
  walk_methods(scope,
      [&](DexMethod *method) {
        if (is_package_protected(method)) set_public(method);
      });
  walk_fields(scope,
      [&](DexField *field) {
        if (is_package_protected(field)) set_public(field);
      });
  for (auto clazz : scope) {
    if (is_package_protected(clazz)) set_public(clazz);
  }
}

bool should_rename(DexClass *clazz,
    std::vector<std::string>& pre_patterns,
    std::vector<std::string>& post_patterns,
    std::unordered_set<const DexType*>& untouchables,
    bool rename_annotations) {
  if (!rename_annotations && is_annotation(clazz)) return false;
  if (untouchables.count(clazz->get_type())) return false;
  auto chstring = clazz->get_type()->get_name()->c_str();
  /* We're assuming anonymous classes are safe always safe to rename */
  auto substr = strrchr(chstring, '$');
  if (substr != nullptr) {
    auto val = *++substr;
    if (val >= '0' && val <= '9') {
      match_inner++;
      return true;
    }
  }
  /* Check for more aggressive, but finer grained filters first */
  for (auto p : pre_patterns) {
    auto substr = strstr(chstring, p.c_str());
    if (substr != nullptr) {
      if (p.length() > 1) {
        match_long++;
      } else {
        match_short++;
      }
      return true;
    }
  }
  if (!can_rename(clazz) && !can_delete(clazz)) {
    return false;
  }
  /* Check for wider, less precise filters */
  for (auto p : post_patterns) {
    auto substr = strstr(chstring, p.c_str());
    if (substr != nullptr) {
      if (p.length() > 1) {
        match_long++;
      } else {
        match_short++;
      }
      return true;
    }
  }
  return false;
}

void rename_classes(
    Scope& scope,
    std::vector<std::string>& pre_whitelist_patterns,
    std::vector<std::string>& post_whitelist_patterns,
    const std::string& path,
    std::unordered_set<const DexType*>& untouchables,
    bool rename_annotations) {
  unpackage_private(scope);
  int clazz_ident = 0;
  std::map<DexString*, DexString*> aliases;
  for(auto clazz: scope) {
    if (!should_rename(
        clazz, pre_whitelist_patterns, post_whitelist_patterns,
        untouchables, rename_annotations)) {
      continue;
    }
    char clzname[4];
    char descriptor[10];
    get_next_ident(clzname, clazz_ident);
    // The X helps our hacked Dalvik classloader recognize that a
    // class name is the output of the redex renamer and thus will
    // never be found in the Android platform.
    sprintf(descriptor, "LX%s;", clzname);
    auto dstring = DexString::make_string(descriptor);
    auto dtype = clazz->get_type();
    auto oldname = dtype->get_name();
    aliases[oldname] = dstring;
    dtype->assign_name_alias(dstring);
    base_strings_size += strlen(oldname->c_str());
    base_strings_size += strlen(dstring->c_str());
    TRACE(RENAME, 4, "'%s'->'%s'\n", oldname->c_str(), descriptor);
    while (1) {
     std::string arrayop("[");
      arrayop += oldname->c_str();
      oldname = DexString::get_string(arrayop.c_str());
      if (oldname == nullptr) {
        break;
      }
      auto arraytype = DexType::get_type(oldname);
      if (arraytype == nullptr) {
        break;
      }
      std::string newarraytype("[");
      newarraytype += dstring->c_str();
      dstring = DexString::make_string(newarraytype.c_str());
      aliases[oldname] = dstring;
      arraytype->assign_name_alias(dstring);
    }
  }
  /* Now we need to re-write the Signature annotations.  They use
   * Strings rather than Type's, so they have to be explicitly
   * handled.
   */

  /* Generics of the form Type<> turn into the Type string
   * sans the ';'.  So, we have to alias those if they
   * exist.  Signature annotations suck.
   */
  for (auto apair : aliases) {
    char buf[MAX_DESCRIPTOR_LENGTH];
    const char *sourcestr = apair.first->c_str();
    size_t sourcelen = strlen(sourcestr);
    if (sourcestr[sourcelen - 1] != ';') continue;
    strcpy(buf, sourcestr);
    buf[sourcelen - 1] = '\0';
    auto dstring = DexString::get_string(buf);
    if (dstring == nullptr) continue;
    strcpy(buf, apair.second->c_str());
    buf[strlen(apair.second->c_str()) - 1] = '\0';
    auto target = DexString::make_string(buf);
    aliases[dstring] = target;
  }
  walk_annotations(scope, [&](DexAnnotation* anno) {
    static DexType *dalviksig =
      DexType::get_type("Ldalvik/annotation/Signature;");
    if (anno->type() != dalviksig) return;
    auto elems = anno->anno_elems();
    for (auto elem : elems) {
      auto ev = elem.encoded_value;
      if (ev->evtype() != DEVT_ARRAY) continue;
      auto arrayev = static_cast<DexEncodedValueArray*>(ev);
      auto const& evs = arrayev->evalues();
      for (auto strev : *evs) {
        if (strev->evtype() != DEVT_STRING) continue;
        auto stringev = static_cast<DexEncodedValueString*>(strev);
        if (aliases.count(stringev->string())) {
          TRACE(RENAME, 5, "Rewriting Signature from '%s' to '%s'\n",
              stringev->string()->c_str(),
              aliases[stringev->string()]->c_str());
          stringev->string(aliases[stringev->string()]);
        }
      }
    }
  });

  if (!path.empty()) {
    FILE* fd = fopen(path.c_str(), "w");
    if (fd == nullptr) {
      perror("Error writing rename file");
      return;
    }
    for (const auto &it : aliases) {
      // record for later processing and back map generation
      fprintf(fd, "%s -> %s\n",it.first->c_str(),
      it.second->c_str());
    }
    fclose(fd);
  }

  for (auto clazz : scope) {
    clazz->get_vmethods().sort(compare_dexmethods);
    clazz->get_dmethods().sort(compare_dexmethods);
    clazz->get_sfields().sort(compare_dexfields);
    clazz->get_ifields().sort(compare_dexfields);
  }
}

void RenameClassesPass::run_pass(DexClassesVector& dexen, ConfigFiles& cfg) {
  auto scope = build_class_scope(dexen);
  std::unordered_set<const DexType*> untouchables;
  for (const auto& base : m_untouchable_hierarchies) {
    auto base_type = DexType::get_type(base.c_str());
    if (base_type != nullptr) {
      untouchables.insert(base_type);
      TypeVector children;
      get_all_children(base_type, children);
      untouchables.insert(children.begin(), children.end());
    }
  }
  rename_classes(
      scope, m_pre_filter_whitelist, m_post_filter_whitelist, m_path,
      untouchables, m_rename_annotations);
  TRACE(RENAME, 1,
      "renamed classes: %d anon classes, %d from single char patterns, "
      "%d from multi char patterns\n",
      match_inner,
      match_short,
      match_long);
  TRACE(RENAME, 1, "String savings, at least %d bytes \n",
      base_strings_size - ren_strings_size);
}
<commit_msg>renamer: preserve a token '$' if original class might have been inner class<commit_after>/**
 * Copyright (c) 2016-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

#include "RenameClasses.h"

#include <algorithm>
#include <map>
#include <string>
#include <vector>
#include <unordered_set>
#include <unordered_map>

#include "walkers.h"
#include "DexClass.h"
#include "DexInstruction.h"
#include "DexUtil.h"
#include "ReachableClasses.h"

#define MAX_DESCRIPTOR_LENGTH (1024)
#define MAX_IDENT_CHAR (52)
#define MAX_IDENT (MAX_IDENT_CHAR * MAX_IDENT_CHAR * MAX_IDENT_CHAR)

int match_short = 0;
int match_long = 0;
int match_inner = 0;

int base_strings_size = 0;
int ren_strings_size = 0;

static char getident(int num) {
  if (num < 26) {
    return 'a' + num;
  } else {
    return 'A' + num - 26;
  }
}

void get_next_ident(char *out, int &num) {
  // *sigh* re-write when not tired.
  int low = num;
  int mid = (num / 52);
  int top = (mid / 52);
  always_assert_log(num <= MAX_IDENT,
                    "Bailing, Ident %d, greater than maximum\n", num);
  if (top) {
    *out++ = getident(top - 1);
    low -= (top *52*52);
  }
  if (mid) {
    mid -= (top * 52);
    *out++ = getident(mid);
    low -= (mid * 52);
  }
  *out++ = getident(low);
  *out++ = '\0';
  num++;
}

void unpackage_private(Scope &scope) {
  walk_methods(scope,
      [&](DexMethod *method) {
        if (is_package_protected(method)) set_public(method);
      });
  walk_fields(scope,
      [&](DexField *field) {
        if (is_package_protected(field)) set_public(field);
      });
  for (auto clazz : scope) {
    if (is_package_protected(clazz)) set_public(clazz);
  }
}

bool should_rename(DexClass *clazz,
    std::vector<std::string>& pre_patterns,
    std::vector<std::string>& post_patterns,
    std::unordered_set<const DexType*>& untouchables,
    bool rename_annotations) {
  if (!rename_annotations && is_annotation(clazz)) return false;
  if (untouchables.count(clazz->get_type())) return false;
  auto chstring = clazz->get_type()->get_name()->c_str();
  /* We're assuming anonymous classes are safe always safe to rename. */
  auto substr = strrchr(chstring, '$');
  if (substr != nullptr) {
    auto val = *++substr;
    if (val >= '0' && val <= '9') {
      match_inner++;
      return true;
    }
  }
  /* Check for more aggressive, but finer grained filters first */
  for (auto p : pre_patterns) {
    auto substr = strstr(chstring, p.c_str());
    if (substr != nullptr) {
      if (p.length() > 1) {
        match_long++;
      } else {
        match_short++;
      }
      return true;
    }
  }
  if (!can_rename(clazz) && !can_delete(clazz)) {
    return false;
  }
  /* Check for wider, less precise filters */
  for (auto p : post_patterns) {
    auto substr = strstr(chstring, p.c_str());
    if (substr != nullptr) {
      if (p.length() > 1) {
        match_long++;
      } else {
        match_short++;
      }
      return true;
    }
  }
  return false;
}

void rename_classes(
    Scope& scope,
    std::vector<std::string>& pre_whitelist_patterns,
    std::vector<std::string>& post_whitelist_patterns,
    const std::string& path,
    std::unordered_set<const DexType*>& untouchables,
    bool rename_annotations) {
  unpackage_private(scope);
  int clazz_ident = 0;
  std::map<DexString*, DexString*> aliases;
  for(auto clazz: scope) {
    if (!should_rename(
        clazz, pre_whitelist_patterns, post_whitelist_patterns,
        untouchables, rename_annotations)) {
      continue;
    }
    char clzname[4];
    get_next_ident(clzname, clazz_ident);

    auto dtype = clazz->get_type();
    auto oldname = dtype->get_name();

    // The X helps our hacked Dalvik classloader recognize that a
    // class name is the output of the redex renamer and thus will
    // never be found in the Android platform.
    // The $ indicates that the class was originally an inner class.
    // Some code, most notably android instrumentation runner, uses
    // this information to decide whether or not to classload the class.
    bool inner = strrchr(oldname->c_str(), '$');
    char descriptor[10];
    sprintf(descriptor, "LX%s%s;", inner ? "$" : "", clzname);
    auto dstring = DexString::make_string(descriptor);
    aliases[oldname] = dstring;
    dtype->assign_name_alias(dstring);
    base_strings_size += strlen(oldname->c_str());
    base_strings_size += strlen(dstring->c_str());
    TRACE(RENAME, 4, "'%s'->'%s'\n", oldname->c_str(), descriptor);
    while (1) {
     std::string arrayop("[");
      arrayop += oldname->c_str();
      oldname = DexString::get_string(arrayop.c_str());
      if (oldname == nullptr) {
        break;
      }
      auto arraytype = DexType::get_type(oldname);
      if (arraytype == nullptr) {
        break;
      }
      std::string newarraytype("[");
      newarraytype += dstring->c_str();
      dstring = DexString::make_string(newarraytype.c_str());
      aliases[oldname] = dstring;
      arraytype->assign_name_alias(dstring);
    }
  }
  /* Now we need to re-write the Signature annotations.  They use
   * Strings rather than Type's, so they have to be explicitly
   * handled.
   */

  /* Generics of the form Type<> turn into the Type string
   * sans the ';'.  So, we have to alias those if they
   * exist.  Signature annotations suck.
   */
  for (auto apair : aliases) {
    char buf[MAX_DESCRIPTOR_LENGTH];
    const char *sourcestr = apair.first->c_str();
    size_t sourcelen = strlen(sourcestr);
    if (sourcestr[sourcelen - 1] != ';') continue;
    strcpy(buf, sourcestr);
    buf[sourcelen - 1] = '\0';
    auto dstring = DexString::get_string(buf);
    if (dstring == nullptr) continue;
    strcpy(buf, apair.second->c_str());
    buf[strlen(apair.second->c_str()) - 1] = '\0';
    auto target = DexString::make_string(buf);
    aliases[dstring] = target;
  }
  walk_annotations(scope, [&](DexAnnotation* anno) {
    static DexType *dalviksig =
      DexType::get_type("Ldalvik/annotation/Signature;");
    if (anno->type() != dalviksig) return;
    auto elems = anno->anno_elems();
    for (auto elem : elems) {
      auto ev = elem.encoded_value;
      if (ev->evtype() != DEVT_ARRAY) continue;
      auto arrayev = static_cast<DexEncodedValueArray*>(ev);
      auto const& evs = arrayev->evalues();
      for (auto strev : *evs) {
        if (strev->evtype() != DEVT_STRING) continue;
        auto stringev = static_cast<DexEncodedValueString*>(strev);
        if (aliases.count(stringev->string())) {
          TRACE(RENAME, 5, "Rewriting Signature from '%s' to '%s'\n",
              stringev->string()->c_str(),
              aliases[stringev->string()]->c_str());
          stringev->string(aliases[stringev->string()]);
        }
      }
    }
  });

  if (!path.empty()) {
    FILE* fd = fopen(path.c_str(), "w");
    if (fd == nullptr) {
      perror("Error writing rename file");
      return;
    }
    for (const auto &it : aliases) {
      // record for later processing and back map generation
      fprintf(fd, "%s -> %s\n",it.first->c_str(),
      it.second->c_str());
    }
    fclose(fd);
  }

  for (auto clazz : scope) {
    clazz->get_vmethods().sort(compare_dexmethods);
    clazz->get_dmethods().sort(compare_dexmethods);
    clazz->get_sfields().sort(compare_dexfields);
    clazz->get_ifields().sort(compare_dexfields);
  }
}

void RenameClassesPass::run_pass(DexClassesVector& dexen, ConfigFiles& cfg) {
  auto scope = build_class_scope(dexen);
  std::unordered_set<const DexType*> untouchables;
  for (const auto& base : m_untouchable_hierarchies) {
    auto base_type = DexType::get_type(base.c_str());
    if (base_type != nullptr) {
      untouchables.insert(base_type);
      TypeVector children;
      get_all_children(base_type, children);
      untouchables.insert(children.begin(), children.end());
    }
  }
  rename_classes(
      scope, m_pre_filter_whitelist, m_post_filter_whitelist, m_path,
      untouchables, m_rename_annotations);
  TRACE(RENAME, 1,
      "renamed classes: %d anon classes, %d from single char patterns, "
      "%d from multi char patterns\n",
      match_inner,
      match_short,
      match_long);
  TRACE(RENAME, 1, "String savings, at least %d bytes \n",
      base_strings_size - ren_strings_size);
}
<|endoftext|>


<commit_before>#include "shpfile.h"
#include <iostream>
#include <vector>
#include <png++/png.hpp>

void ShpFile::save_as_png(int idx, const std::string& filename)
{
	std::cout << "Saving image " << idx << " to " << filename << std::endl;
	images[idx].write(filename);
}

std::istream& operator>>(std::istream& is, ShpFile& file)
{
	// Blade Runner SHP is different from previous Westwood SHP files.
	// Each file starts with the number of images:
	is.read((char*)&file.num_images, sizeof(int32_t));

	// Each image has a header followed by the raw, high-color pixel data.
	for (int i = 0; i < file.num_images; i++)
	{
		ShpHeader info;
		is.read((char*)&info, sizeof(ShpHeader));

		png::image<png::rgb_pixel> img(info.width, info.height);
		int16_t color;
		for (int y = 0; y < info.height; y++)
		{
			for (int x = 0; x < info.width; x++)
			{
				is.read((char*)&color, sizeof(int16_t));
				int8_t r = color >> 7 & 0xff;
				int8_t g = color >> 2 & 0xff;
				int8_t b = color << 3 & 0xff;
				img.set_pixel(x, y, png::rgb_pixel(r, g, b));
			}
		}

		file.images.push_back(img);
	}

	return is;
}
<commit_msg>Added comment<commit_after>#include "shpfile.h"
#include <iostream>
#include <vector>
#include <png++/png.hpp>

void ShpFile::save_as_png(int idx, const std::string& filename)
{
	std::cout << "Saving image " << idx << " to " << filename << std::endl;
	images[idx].write(filename);
}

// TODO: can we write to png buffer directly?
std::istream& operator>>(std::istream& is, ShpFile& file)
{
	// Blade Runner SHP is different from previous Westwood SHP files.
	// Each file starts with the number of images:
	is.read((char*)&file.num_images, sizeof(int32_t));

	// Each image has a header followed by the raw, high-color pixel data.
	for (int i = 0; i < file.num_images; i++)
	{
		ShpHeader info;
		is.read((char*)&info, sizeof(ShpHeader));

		png::image<png::rgb_pixel> img(info.width, info.height);
		int16_t color;
		for (int y = 0; y < info.height; y++)
		{
			for (int x = 0; x < info.width; x++)
			{
				is.read((char*)&color, sizeof(int16_t));
				int8_t r = color >> 7 & 0xff;
				int8_t g = color >> 2 & 0xff;
				int8_t b = color << 3 & 0xff;
				img.set_pixel(x, y, png::rgb_pixel(r, g, b));
			}
		}

		file.images.push_back(img);
	}

	return is;
}
<|endoftext|>


<commit_before><commit_msg>Attempts to eliminate all remaining type variations.<commit_after><|endoftext|>


<commit_before><commit_msg>ReadPSD: support for icc default profiles (rgb/cmyk/gray), blackpoint, rendering intent. issue #130<commit_after><|endoftext|>


<commit_before>#include "istream/istream_chunked.hxx"
#include "istream/istream_string.hxx"
#include "istream/istream_internal.hxx"
#include "pool.hxx"

static struct istream *
create_input(struct pool *pool)
{
    return istream_string_new(pool, "foo_bar_0123456789abcdefghijklmnopqrstuvwxyz");
}

static struct istream *
create_test(struct pool *pool, struct istream *input)
{
    return istream_chunked_new(pool, input);
}

#define CUSTOM_TEST

struct custom {
    struct istream output;

    bool eof;
    GError *error;
};

/*
 * istream handler
 *
 */

static size_t
custom_istream_data(gcc_unused const void *data, gcc_unused size_t length,
                    void *_ctx)
{
    auto *ctx = (struct custom *)_ctx;

    istream_invoke_data(&ctx->output, " ", 1);
    return 0;
}

static void
custom_istream_eof(void *_ctx)
{
    auto *ctx = (struct custom *)_ctx;

    ctx->eof = true;
}

static void
custom_istream_abort(GError *error, void *_ctx)
{
    auto *ctx = (struct custom *)_ctx;

    ctx->error = error;
}

static const struct istream_handler custom_istream_handler = {
    .data = custom_istream_data,
    .direct = nullptr,
    .eof = custom_istream_eof,
    .abort = custom_istream_abort,
};

/*
 * istream class
 *
 */

static off_t
istream_custom_available(gcc_unused struct istream *istream,
                         gcc_unused bool partial)
{
    return 1;
}

static void
istream_custom_read(gcc_unused struct istream *istream)
{
}

static void
istream_custom_close(struct istream *istream)
{
    istream_deinit(istream);
}

static const struct istream_class istream_custom = {
    .available = istream_custom_available,
    .skip = nullptr,
    .read = istream_custom_read,
    .as_fd = nullptr,
    .close = istream_custom_close,
};

static void
test_custom(struct pool *pool)
{
    pool = pool_new_linear(pool, "test", 8192);
    auto *ctx = NewFromPool<struct custom>(*pool);
    istream_init(&ctx->output, &istream_custom, pool);

    auto *chunked = istream_chunked_new(pool, &ctx->output);
    istream_handler_set(chunked, &custom_istream_handler, ctx, 0);
    pool_unref(pool);

    istream_read(chunked);
    istream_close(chunked);

    pool_commit();
}

#include "t_istream_filter.hxx"
<commit_msg>test/t_istream_chunked: rename struct custom to Custom<commit_after>#include "istream/istream_chunked.hxx"
#include "istream/istream_string.hxx"
#include "istream/istream_internal.hxx"
#include "pool.hxx"

static struct istream *
create_input(struct pool *pool)
{
    return istream_string_new(pool, "foo_bar_0123456789abcdefghijklmnopqrstuvwxyz");
}

static struct istream *
create_test(struct pool *pool, struct istream *input)
{
    return istream_chunked_new(pool, input);
}

#define CUSTOM_TEST

struct Custom {
    struct istream output;

    bool eof;
    GError *error;
};

/*
 * istream handler
 *
 */

static size_t
custom_istream_data(gcc_unused const void *data, gcc_unused size_t length,
                    void *_ctx)
{
    auto *ctx = (Custom *)_ctx;

    istream_invoke_data(&ctx->output, " ", 1);
    return 0;
}

static void
custom_istream_eof(void *_ctx)
{
    auto *ctx = (Custom *)_ctx;

    ctx->eof = true;
}

static void
custom_istream_abort(GError *error, void *_ctx)
{
    auto *ctx = (Custom *)_ctx;

    ctx->error = error;
}

static const struct istream_handler custom_istream_handler = {
    .data = custom_istream_data,
    .direct = nullptr,
    .eof = custom_istream_eof,
    .abort = custom_istream_abort,
};

/*
 * istream class
 *
 */

static off_t
istream_custom_available(gcc_unused struct istream *istream,
                         gcc_unused bool partial)
{
    return 1;
}

static void
istream_custom_read(gcc_unused struct istream *istream)
{
}

static void
istream_custom_close(struct istream *istream)
{
    istream_deinit(istream);
}

static const struct istream_class istream_custom = {
    .available = istream_custom_available,
    .skip = nullptr,
    .read = istream_custom_read,
    .as_fd = nullptr,
    .close = istream_custom_close,
};

static void
test_custom(struct pool *pool)
{
    pool = pool_new_linear(pool, "test", 8192);
    auto *ctx = NewFromPool<Custom>(*pool);
    istream_init(&ctx->output, &istream_custom, pool);

    auto *chunked = istream_chunked_new(pool, &ctx->output);
    istream_handler_set(chunked, &custom_istream_handler, ctx, 0);
    pool_unref(pool);

    istream_read(chunked);
    istream_close(chunked);

    pool_commit();
}

#include "t_istream_filter.hxx"
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "assembler_x86.h"
#include "jni_internal.h"
#include "object.h"
#include "stack_indirect_reference_table.h"

#define __ assembler->

namespace art {
namespace x86 {

ByteArray* X86CreateResolutionTrampoline(Runtime::TrampolineType) {
  UniquePtr<X86Assembler> assembler(static_cast<X86Assembler*>(Assembler::Create(kX86)));

  // TODO: unimplemented
  __ int3();

  assembler->EmitSlowPaths();
  size_t cs = assembler->CodeSize();
  SirtRef<ByteArray> resolution_trampoline(ByteArray::Alloc(cs));
  CHECK(resolution_trampoline.get() != NULL);
  MemoryRegion code(resolution_trampoline->GetData(), resolution_trampoline->GetLength());
  assembler->FinalizeInstructions(code);

  return resolution_trampoline.get();
}

typedef void (*ThrowAme)(Method*, Thread*);

ByteArray* CreateAbstractMethodErrorStub() {
  UniquePtr<X86Assembler> assembler(static_cast<X86Assembler*>(Assembler::Create(kX86)));

  // return address
  __ pushl(EDI);
  __ pushl(ESI);
  __ pushl(EBP);
  __ pushl(Immediate(0));
  __ pushl(Immediate(0));
  __ pushl(Immediate(0));
  __ pushl(Immediate(0));  // <-- callee save Method* to go here
  __ movl(ECX, ESP);       // save ESP
  __ pushl(Immediate(0));  // align frame
  __ pushl(ECX);           // pass ESP for Method*
  __ fs()->pushl(Address::Absolute(Thread::SelfOffset()));  // Thread*
  __ pushl(EAX);           // pass Method*

  // Call to throw AbstractMethodError.
  __ Call(ThreadOffset(OFFSETOF_MEMBER(Thread, pThrowAbstractMethodErrorFromCode)),
          X86ManagedRegister::FromCpuRegister(ECX));

#if defined(ART_USE_LLVM_COMPILER)
  // Return to caller who will handle pending exception.
  __ addl(ESP, Immediate(28));
  __ popl(EBX);
  __ popl(EBP);
  __ popl(ESI);
  __ popl(EDI);
  __ ret();
#else
  // Call never returns.
  __ int3();
#endif

  assembler->EmitSlowPaths();

  size_t cs = assembler->CodeSize();
  SirtRef<ByteArray> abstract_stub(ByteArray::Alloc(cs));
  CHECK(abstract_stub.get() != NULL);
  MemoryRegion code(abstract_stub->GetData(), abstract_stub->GetLength());
  assembler->FinalizeInstructions(code);

  return abstract_stub.get();
}

ByteArray* CreateJniDlsymLookupStub() {
  UniquePtr<X86Assembler> assembler(static_cast<X86Assembler*>(Assembler::Create(kX86)));

  // Pad stack to ensure 16-byte alignment
  __ pushl(Immediate(0));
  __ pushl(Immediate(0));
  __ fs()->pushl(Address::Absolute(Thread::SelfOffset()));  // Thread*

  __ Call(ThreadOffset(OFFSETOF_MEMBER(Thread, pFindNativeMethod)),
          X86ManagedRegister::FromCpuRegister(ECX));

  __ addl(ESP, Immediate(12));

  Label no_native_code_found;  // forward declaration
  __ cmpl(EAX, Immediate(0));
  __ j(kEqual, &no_native_code_found);

  __ jmp(EAX);  // Tail call into native code

  __ Bind(&no_native_code_found);
  __ ret(); // return to caller to handle exception

  assembler->EmitSlowPaths();

  size_t cs = assembler->CodeSize();
  SirtRef<ByteArray> jni_stub(ByteArray::Alloc(cs));
  CHECK(jni_stub.get() != NULL);
  MemoryRegion code(jni_stub->GetData(), jni_stub->GetLength());
  assembler->FinalizeInstructions(code);

  return jni_stub.get();
}

} // namespace x86
} // namespace art
<commit_msg>Fix compiler_test for the LLVM route.<commit_after>/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "assembler_x86.h"
#include "jni_internal.h"
#include "object.h"
#include "stack_indirect_reference_table.h"

#define __ assembler->

namespace art {
namespace x86 {

ByteArray* X86CreateResolutionTrampoline(Runtime::TrampolineType) {
  UniquePtr<X86Assembler> assembler(static_cast<X86Assembler*>(Assembler::Create(kX86)));

  // TODO: unimplemented
  __ int3();

  assembler->EmitSlowPaths();
  size_t cs = assembler->CodeSize();
  SirtRef<ByteArray> resolution_trampoline(ByteArray::Alloc(cs));
  CHECK(resolution_trampoline.get() != NULL);
  MemoryRegion code(resolution_trampoline->GetData(), resolution_trampoline->GetLength());
  assembler->FinalizeInstructions(code);

  return resolution_trampoline.get();
}

typedef void (*ThrowAme)(Method*, Thread*);

ByteArray* CreateAbstractMethodErrorStub() {
  UniquePtr<X86Assembler> assembler(static_cast<X86Assembler*>(Assembler::Create(kX86)));

  // return address
  __ pushl(EDI);
  __ pushl(ESI);
  __ pushl(EBP);
  __ pushl(Immediate(0));
  __ pushl(Immediate(0));
  __ pushl(Immediate(0));
  __ pushl(Immediate(0));  // <-- callee save Method* to go here
  __ movl(ECX, ESP);       // save ESP
  __ pushl(Immediate(0));  // align frame
  __ pushl(ECX);           // pass ESP for Method*
  __ fs()->pushl(Address::Absolute(Thread::SelfOffset()));  // Thread*
  __ pushl(EAX);           // pass Method*

  // Call to throw AbstractMethodError.
  __ Call(ThreadOffset(OFFSETOF_MEMBER(Thread, pThrowAbstractMethodErrorFromCode)),
          X86ManagedRegister::FromCpuRegister(ECX));

#if defined(ART_USE_LLVM_COMPILER)
  // Return to caller who will handle pending exception.
  __ addl(ESP, Immediate(32));
  __ popl(EBP);
  __ popl(ESI);
  __ popl(EDI);
  __ ret();
#else
  // Call never returns.
  __ int3();
#endif

  assembler->EmitSlowPaths();

  size_t cs = assembler->CodeSize();
  SirtRef<ByteArray> abstract_stub(ByteArray::Alloc(cs));
  CHECK(abstract_stub.get() != NULL);
  MemoryRegion code(abstract_stub->GetData(), abstract_stub->GetLength());
  assembler->FinalizeInstructions(code);

  return abstract_stub.get();
}

ByteArray* CreateJniDlsymLookupStub() {
  UniquePtr<X86Assembler> assembler(static_cast<X86Assembler*>(Assembler::Create(kX86)));

  // Pad stack to ensure 16-byte alignment
  __ pushl(Immediate(0));
  __ pushl(Immediate(0));
  __ fs()->pushl(Address::Absolute(Thread::SelfOffset()));  // Thread*

  __ Call(ThreadOffset(OFFSETOF_MEMBER(Thread, pFindNativeMethod)),
          X86ManagedRegister::FromCpuRegister(ECX));

  __ addl(ESP, Immediate(12));

  Label no_native_code_found;  // forward declaration
  __ cmpl(EAX, Immediate(0));
  __ j(kEqual, &no_native_code_found);

  __ jmp(EAX);  // Tail call into native code

  __ Bind(&no_native_code_found);
  __ ret(); // return to caller to handle exception

  assembler->EmitSlowPaths();

  size_t cs = assembler->CodeSize();
  SirtRef<ByteArray> jni_stub(ByteArray::Alloc(cs));
  CHECK(jni_stub.get() != NULL);
  MemoryRegion code(jni_stub->GetData(), jni_stub->GetLength());
  assembler->FinalizeInstructions(code);

  return jni_stub.get();
}

} // namespace x86
} // namespace art
<|endoftext|>


<commit_before>#include "mswin.h"
#include "systems.h"
#include "nodes/system.h"
#include "graphics.h"
#include "vector.h"
#include "memory.h"
#include "compiler.h"
#include "aligned-arrays.h"
#include <cassert>

namespace
{
  NOINLINE
  auto append (float (* & out) [4], const float (* from) [3], unsigned count) -> float (*) [4]
  {
    count &= ~1; // Declare that count is even.
    for (unsigned n = 0; n != count; ++ n) {
      out [n] [0] = from [n] [0];
      out [n] [1] = from [n] [1];
      out [n] [2] = from [n] [2];
      out [n] [3] = 0.0f;
    }
    float (* result) [4] = out;
    out += count;
    return result;
  }

  void assign (float (* out) [4], const float (* from) [3], unsigned count)
  {
     for (unsigned n = 0; n != count; ++ n) {
      out [n] [0] = from [n] [0];
      out [n] [1] = from [n] [1];
      out [n] [2] = from [n] [2];
      out [n] [3] = 0.0f;
    }
  }

  unsigned make_vao (float (* vertices) [4], std::uint8_t (* indices) [6],
                     unsigned p, unsigned q, unsigned r,
                     const float (* x) [3], const float (* y) [3], const float (* z) [3],
                     const std::uint8_t * P, const std::uint8_t * Q, const std::uint8_t * R,
                     const std::uint8_t * X, const std::uint8_t * Y, const std::uint8_t * Z)
  {
    unsigned  N = 2 * p*q*r / (q*r + r*p + p*q - p*q*r);

    float (* out) [4] = vertices;
    append (out, x, N / p);
    append (out, y, N / q);
    append (out, z, N / r);

    for (unsigned n = 0; n != N; ++ n) {
      unsigned i = n;
      unsigned j = i [R];
      unsigned k = j [P];
      assert (i == k [Q]);
      indices [n] [0] = X [k];
      indices [n] [1] = Y [j] + N / p;
      indices [n] [2] = Z [j] + N / p + N / q;
      indices [n] [3] = X [i];
      indices [n] [4] = Y [i] + N / p;
      indices [n] [5] = Z [k] + N / p + N / q;
    }
    return data_to_vao_id (N, vertices, indices);
  }

  template <unsigned q, unsigned r>
  void initialize_system (float (* vertices) [4], std::uint8_t (* indices) [6],
                          float (& abc) [system_count] [8] [4],
                          float (& xyz) [system_count] [3] [4],
                          unsigned (& primitive_count) [system_count],
                          unsigned (& vao_ids) [system_count],
                          const system_t <q, r> & system,
                          system_select_t select)
  {
    assign (abc [select], system.g, 8);
    assign (& xyz [select] [0], system.x, 1);
    assign (& xyz [select] [1], system.y, 1);
    assign (& xyz [select] [2], system.z, 1);
    primitive_count [select] = system.N;
    vao_ids [select] = make_vao (vertices, indices,
                                 2, q, r,
                                 system.x, system.y, system.z,
                                 system.P, system.Q, system.R,
                                 system.X, system.Y, system.Z);
  }
}

void initialize_systems (float (& abc) [system_count] [8] [4],
                         float (& xyz) [system_count] [3] [4],
                         unsigned (& primitive_count) [system_count],
                         unsigned (& vao_ids) [system_count])
{
  const void * data = get_resource_data (256, nullptr);
  system_t <3, 3> const & t (* reinterpret_cast <system_t <3, 3> const *> (data));
  system_t <3, 4> const & o (* reinterpret_cast <system_t <3, 4> const *> (& t + 1));
  system_t <3, 5> const & i (* reinterpret_cast <system_t <3, 5> const *> (& o + 1));

  void  * memory = nullptr;
  unsigned capacity = 0;
  float (* vertices) [4] = nullptr;
  std::uint8_t (* indices) [6] = nullptr;
  reallocate_aligned_arrays (memory, capacity, i.N + 2, & vertices, & indices);

  initialize_system (vertices, indices, abc, xyz, primitive_count, vao_ids, t, tetrahedral);
  initialize_system (vertices, indices, abc, xyz, primitive_count, vao_ids, o, octahedral);
  initialize_system (vertices, indices, abc, xyz, primitive_count, vao_ids, i, icosahedral);

  deallocate (memory);
}
<commit_msg>Add NOINLINE attribute (prevents loop unrolling to reduce object size for non-performance critical initialization code).<commit_after>#include "mswin.h"
#include "systems.h"
#include "nodes/system.h"
#include "graphics.h"
#include "vector.h"
#include "memory.h"
#include "compiler.h"
#include "aligned-arrays.h"
#include <cassert>

namespace
{
  NOINLINE
  auto append (float (* & out) [4], const float (* from) [3], unsigned count) -> float (*) [4]
  {
    count &= ~1; // Declare that count is even.
    for (unsigned n = 0; n != count; ++ n) {
      out [n] [0] = from [n] [0];
      out [n] [1] = from [n] [1];
      out [n] [2] = from [n] [2];
      out [n] [3] = 0.0f;
    }
    float (* result) [4] = out;
    out += count;
    return result;
  }

  NOINLINE
  void assign (float (* out) [4], const float (* from) [3], unsigned count)
  {
     for (unsigned n = 0; n != count; ++ n) {
      out [n] [0] = from [n] [0];
      out [n] [1] = from [n] [1];
      out [n] [2] = from [n] [2];
      out [n] [3] = 0.0f;
    }
  }

  NOINLINE
  unsigned make_vao (float (* vertices) [4], std::uint8_t (* indices) [6],
                     unsigned p, unsigned q, unsigned r,
                     const float (* x) [3], const float (* y) [3], const float (* z) [3],
                     const std::uint8_t * P, const std::uint8_t * Q, const std::uint8_t * R,
                     const std::uint8_t * X, const std::uint8_t * Y, const std::uint8_t * Z)
  {
    unsigned  N = 2 * p*q*r / (q*r + r*p + p*q - p*q*r);

    float (* out) [4] = vertices;
    append (out, x, N / p);
    append (out, y, N / q);
    append (out, z, N / r);

    for (unsigned n = 0; n != N; ++ n) {
      unsigned i = n;
      unsigned j = i [R];
      unsigned k = j [P];
      assert (i == k [Q]);
      indices [n] [0] = X [k];
      indices [n] [1] = Y [j] + N / p;
      indices [n] [2] = Z [j] + N / p + N / q;
      indices [n] [3] = X [i];
      indices [n] [4] = Y [i] + N / p;
      indices [n] [5] = Z [k] + N / p + N / q;
    }
    return data_to_vao_id (N, vertices, indices);
  }

  template <unsigned q, unsigned r>
  void initialize_system (float (* vertices) [4], std::uint8_t (* indices) [6],
                          float (& abc) [system_count] [8] [4],
                          float (& xyz) [system_count] [3] [4],
                          unsigned (& primitive_count) [system_count],
                          unsigned (& vao_ids) [system_count],
                          const system_t <q, r> & system,
                          system_select_t select)
  {
    assign (abc [select], system.g, 8);
    assign (& xyz [select] [0], system.x, 1);
    assign (& xyz [select] [1], system.y, 1);
    assign (& xyz [select] [2], system.z, 1);
    primitive_count [select] = system.N;
    vao_ids [select] = make_vao (vertices, indices,
                                 2, q, r,
                                 system.x, system.y, system.z,
                                 system.P, system.Q, system.R,
                                 system.X, system.Y, system.Z);
  }
}

void initialize_systems (float (& abc) [system_count] [8] [4],
                         float (& xyz) [system_count] [3] [4],
                         unsigned (& primitive_count) [system_count],
                         unsigned (& vao_ids) [system_count])
{
  const void * data = get_resource_data (256, nullptr);
  system_t <3, 3> const & t (* reinterpret_cast <system_t <3, 3> const *> (data));
  system_t <3, 4> const & o (* reinterpret_cast <system_t <3, 4> const *> (& t + 1));
  system_t <3, 5> const & i (* reinterpret_cast <system_t <3, 5> const *> (& o + 1));

  void  * memory = nullptr;
  unsigned capacity = 0;
  float (* vertices) [4] = nullptr;
  std::uint8_t (* indices) [6] = nullptr;
  reallocate_aligned_arrays (memory, capacity, i.N + 2, & vertices, & indices);

  initialize_system (vertices, indices, abc, xyz, primitive_count, vao_ids, t, tetrahedral);
  initialize_system (vertices, indices, abc, xyz, primitive_count, vao_ids, o, octahedral);
  initialize_system (vertices, indices, abc, xyz, primitive_count, vao_ids, i, icosahedral);

  deallocate (memory);
}
<|endoftext|>


<commit_before>#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>

#include "trianglemesh.hpp"

BOOST_AUTO_TEST_SUITE(trianglemesh_test_suite)

BOOST_AUTO_TEST_CASE(declare_test) {
	// check that declarations work as expected
	BOOST_CHECK_NO_THROW(Edge edge(0, 1));
	BOOST_CHECK_NO_THROW(Face face(0, 1, 2));
	BOOST_CHECK_NO_THROW(Mesh mesh);

	// degenerate edges and faces
	BOOST_CHECK_THROW(Edge edge(20, 20), std::runtime_error);
	BOOST_CHECK_THROW(Face face(30, 0, 30), std::runtime_error);
	BOOST_CHECK_THROW(Face face(0, 40, 40), std::runtime_error);
	BOOST_CHECK_THROW(Face face(50, 50, 0), std::runtime_error);
	BOOST_CHECK_THROW(Face face(7, 7, 7), std::runtime_error);
}

BOOST_AUTO_TEST_CASE(edge_index_test_0) {
	Edge edge(10, 20);
	BOOST_CHECK_EQUAL(edge.ev0, 10);
	BOOST_CHECK_EQUAL(edge.ev1, 20);
}

BOOST_AUTO_TEST_CASE(edge_index_test_1) {
	Edge edge(20, 10);
	BOOST_CHECK_EQUAL(edge.ev0, 10);
	BOOST_CHECK_EQUAL(edge.ev1, 20);
}

BOOST_AUTO_TEST_CASE(edge_common_test_0) {
	Edge e1(10, 11);
	Edge e2(12, 13);
	std::vector<int> common = e1.get_common_vertices(e2);
	BOOST_CHECK_EQUAL(common.size(), 0);
}

BOOST_AUTO_TEST_CASE(edge_common_test_1) {
	Edge e1(10, 11);
	Edge e2(10, 13);
	std::vector<int> common = e1.get_common_vertices(e2);
	BOOST_CHECK_EQUAL(common.size(), 1);
	BOOST_CHECK_EQUAL(common[0], 10);
}

BOOST_AUTO_TEST_CASE(edge_common_test_2) {
	Edge e1(10, 13);
	Edge e2(10, 13);
	std::vector<int> common = e1.get_common_vertices(e2);
	BOOST_CHECK_EQUAL(common.size(), 2);
	BOOST_CHECK_EQUAL(common[0], 10);
	BOOST_CHECK_EQUAL(common[1], 13);
}

BOOST_AUTO_TEST_CASE(face_winding_test) {
	Face f(5, 6, 7);
	// throw on illegal index?
	BOOST_CHECK_THROW(f.get_vertex_winding(5, 8), std::runtime_error);
	BOOST_CHECK_THROW(f.get_vertex_winding(8, 5), std::runtime_error);
	BOOST_CHECK_THROW(f.get_vertex_winding(8, 9), std::runtime_error);
	// correct winding?
	BOOST_CHECK_EQUAL(f.get_vertex_winding(5, 6), 0);
	BOOST_CHECK_EQUAL(f.get_vertex_winding(6, 7), 0);
	BOOST_CHECK_EQUAL(f.get_vertex_winding(7, 5), 0);
	BOOST_CHECK_EQUAL(f.get_vertex_winding(6, 5), 1);
	BOOST_CHECK_EQUAL(f.get_vertex_winding(7, 6), 1);
	BOOST_CHECK_EQUAL(f.get_vertex_winding(5, 7), 1);
}

BOOST_AUTO_TEST_CASE(face_next_vertex_test) {
	Face f(9, 3, 18);
	// throw on illegal index?
	BOOST_CHECK_THROW(f.get_next_vertex(5), std::runtime_error);
	// correct next vertex?
	BOOST_CHECK_EQUAL(f.get_next_vertex(9), 3);
	BOOST_CHECK_EQUAL(f.get_next_vertex(3), 18);
	BOOST_CHECK_EQUAL(f.get_next_vertex(18), 9);
}

BOOST_AUTO_TEST_CASE(face_other_vertex_test) {
	Face f(6, 2, 5);
	// throw on illegal index?
	BOOST_CHECK_THROW(f.get_other_vertex(6, 20), std::runtime_error);
	BOOST_CHECK_THROW(f.get_other_vertex(60, 2), std::runtime_error);
	BOOST_CHECK_THROW(f.get_other_vertex(60, 20), std::runtime_error);
	// correct other vertex?
	BOOST_CHECK_EQUAL(f.get_other_vertex(6, 2), 5);
	BOOST_CHECK_EQUAL(f.get_other_vertex(2, 6), 5);
	BOOST_CHECK_EQUAL(f.get_other_vertex(2, 5), 6);
	BOOST_CHECK_EQUAL(f.get_other_vertex(5, 2), 6);
	BOOST_CHECK_EQUAL(f.get_other_vertex(6, 5), 2);
	BOOST_CHECK_EQUAL(f.get_other_vertex(5, 6), 2);
}

BOOST_AUTO_TEST_CASE(face_vertex_order_test_0) {
	Face f(6, 2, 5);
	BOOST_CHECK_EQUAL(f.v0, 2);
	BOOST_CHECK_EQUAL(f.v1, 5);
	BOOST_CHECK_EQUAL(f.v2, 6);
}

BOOST_AUTO_TEST_CASE(face_vertex_order_test_1) {
	Face f(2, 5, 6);
	BOOST_CHECK_EQUAL(f.v0, 2);
	BOOST_CHECK_EQUAL(f.v1, 5);
	BOOST_CHECK_EQUAL(f.v2, 6);
}

BOOST_AUTO_TEST_CASE(face_vertex_order_test_2) {
	Face f(5, 6, 2);
	BOOST_CHECK_EQUAL(f.v0, 2);
	BOOST_CHECK_EQUAL(f.v1, 5);
	BOOST_CHECK_EQUAL(f.v2, 6);
}

BOOST_AUTO_TEST_CASE(add_face_test) {
	Mesh m;

	// add faces
	BOOST_CHECK_NO_THROW(m.add_face(0, 1, 2));
	BOOST_CHECK_NO_THROW(m.add_face(2, 1, 3));
	BOOST_CHECK_NO_THROW(m.add_face(2, 3, 4));
	// add duplicate face
	BOOST_CHECK_NO_THROW(m.add_face(2, 3, 4));
	boost::shared_ptr<MFace> f0 = m.add_face(10, 11, 12);
	boost::shared_ptr<MFace> f1 = m.add_face(12, 10, 11);
	boost::shared_ptr<MFace> f2 = m.add_face(11, 12, 10);
	BOOST_CHECK_EQUAL(f0, f1);
	BOOST_CHECK_EQUAL(f0, f2);
}

BOOST_AUTO_TEST_CASE(face_get_edge_test) {
	// construct mesh
	Mesh m;
	boost::shared_ptr<MFace> f0 = m.add_face(0, 1, 2);
	boost::shared_ptr<MFace> f1 = m.add_face(2, 1, 3);
	boost::shared_ptr<MFace> f2 = m.add_face(2, 3, 4);
	// throw on illegal index?
	BOOST_CHECK_THROW(f0->get_edge(0, 3), std::runtime_error);
	BOOST_CHECK_THROW(f0->get_edge(3, 0), std::runtime_error);
	BOOST_CHECK_THROW(f0->get_edge(3, 4), std::runtime_error);
	// correct edge?
	boost::shared_ptr<const MEdge> e0, e1;
	e0 = f0->get_edge(0, 1);
	e1 = f0->get_edge(1, 0);
	BOOST_CHECK_EQUAL(e0, e1);
	e0 = f0->get_edge(0, 1);
	e1 = f0->get_edge(1, 2);
	BOOST_CHECK(e0 != e1);
	e0 = f0->get_edge(1, 2);
	e1 = f1->get_edge(1, 2);
	BOOST_CHECK_EQUAL(e0, e1);
	BOOST_CHECK_EQUAL(e0->ev0, 1);
	BOOST_CHECK_EQUAL(e0->ev1, 2);
	BOOST_CHECK_EQUAL(e1->ev0, 1);
	BOOST_CHECK_EQUAL(e1->ev1, 2);
	e0 = f1->get_edge(2, 3);
	e1 = f2->get_edge(2, 3);
	BOOST_CHECK_EQUAL(e0, e1);
	BOOST_CHECK_EQUAL(e0->ev0, 2);
	BOOST_CHECK_EQUAL(e0->ev1, 3);
	BOOST_CHECK_EQUAL(e1->ev0, 2);
	BOOST_CHECK_EQUAL(e1->ev1, 3);
}

BOOST_AUTO_TEST_CASE(face_get_common_edges_test) {
	// construct mesh
	Mesh m;
	boost::shared_ptr<MFace> f0;
	boost::shared_ptr<MFace> f1;
	boost::shared_ptr<MFace> f2;
	f0 = m.add_face(0, 1, 2);
	f1 = m.add_face(2, 1, 3);
	f2 = m.add_face(2, 3, 4);
	// correct edge?
	std::vector<boost::shared_ptr<const MEdge> > common;
	common = f0->get_common_edges(*f2);
	BOOST_CHECK_EQUAL(common.size(), 0);
	common = f0->get_common_edges(*f1);
	BOOST_CHECK_EQUAL(common.size(), 1);
	BOOST_CHECK_EQUAL(common[0]->ev0, 1);
	BOOST_CHECK_EQUAL(common[0]->ev1, 2);
	common = f0->get_common_edges(*f0);
	BOOST_CHECK_EQUAL(common.size(), 3);
	BOOST_CHECK_EQUAL(common[0]->ev0, 0);
	BOOST_CHECK_EQUAL(common[0]->ev1, 1);
	BOOST_CHECK_EQUAL(common[1]->ev0, 1);
	BOOST_CHECK_EQUAL(common[1]->ev1, 2);
	BOOST_CHECK_EQUAL(common[2]->ev0, 0);
	BOOST_CHECK_EQUAL(common[2]->ev1, 2);
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>Test number of faces and edges, when duplicates are around.<commit_after>#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>

#include "trianglemesh.hpp"

BOOST_AUTO_TEST_SUITE(trianglemesh_test_suite)

BOOST_AUTO_TEST_CASE(declare_test) {
	// check that declarations work as expected
	BOOST_CHECK_NO_THROW(Edge edge(0, 1));
	BOOST_CHECK_NO_THROW(Face face(0, 1, 2));
	BOOST_CHECK_NO_THROW(Mesh mesh);

	// degenerate edges and faces
	BOOST_CHECK_THROW(Edge edge(20, 20), std::runtime_error);
	BOOST_CHECK_THROW(Face face(30, 0, 30), std::runtime_error);
	BOOST_CHECK_THROW(Face face(0, 40, 40), std::runtime_error);
	BOOST_CHECK_THROW(Face face(50, 50, 0), std::runtime_error);
	BOOST_CHECK_THROW(Face face(7, 7, 7), std::runtime_error);
}

BOOST_AUTO_TEST_CASE(edge_index_test_0) {
	Edge edge(10, 20);
	BOOST_CHECK_EQUAL(edge.ev0, 10);
	BOOST_CHECK_EQUAL(edge.ev1, 20);
}

BOOST_AUTO_TEST_CASE(edge_index_test_1) {
	Edge edge(20, 10);
	BOOST_CHECK_EQUAL(edge.ev0, 10);
	BOOST_CHECK_EQUAL(edge.ev1, 20);
}

BOOST_AUTO_TEST_CASE(edge_common_test_0) {
	Edge e1(10, 11);
	Edge e2(12, 13);
	std::vector<int> common = e1.get_common_vertices(e2);
	BOOST_CHECK_EQUAL(common.size(), 0);
}

BOOST_AUTO_TEST_CASE(edge_common_test_1) {
	Edge e1(10, 11);
	Edge e2(10, 13);
	std::vector<int> common = e1.get_common_vertices(e2);
	BOOST_CHECK_EQUAL(common.size(), 1);
	BOOST_CHECK_EQUAL(common[0], 10);
}

BOOST_AUTO_TEST_CASE(edge_common_test_2) {
	Edge e1(10, 13);
	Edge e2(10, 13);
	std::vector<int> common = e1.get_common_vertices(e2);
	BOOST_CHECK_EQUAL(common.size(), 2);
	BOOST_CHECK_EQUAL(common[0], 10);
	BOOST_CHECK_EQUAL(common[1], 13);
}

BOOST_AUTO_TEST_CASE(face_winding_test) {
	Face f(5, 6, 7);
	// throw on illegal index?
	BOOST_CHECK_THROW(f.get_vertex_winding(5, 8), std::runtime_error);
	BOOST_CHECK_THROW(f.get_vertex_winding(8, 5), std::runtime_error);
	BOOST_CHECK_THROW(f.get_vertex_winding(8, 9), std::runtime_error);
	// correct winding?
	BOOST_CHECK_EQUAL(f.get_vertex_winding(5, 6), 0);
	BOOST_CHECK_EQUAL(f.get_vertex_winding(6, 7), 0);
	BOOST_CHECK_EQUAL(f.get_vertex_winding(7, 5), 0);
	BOOST_CHECK_EQUAL(f.get_vertex_winding(6, 5), 1);
	BOOST_CHECK_EQUAL(f.get_vertex_winding(7, 6), 1);
	BOOST_CHECK_EQUAL(f.get_vertex_winding(5, 7), 1);
}

BOOST_AUTO_TEST_CASE(face_next_vertex_test) {
	Face f(9, 3, 18);
	// throw on illegal index?
	BOOST_CHECK_THROW(f.get_next_vertex(5), std::runtime_error);
	// correct next vertex?
	BOOST_CHECK_EQUAL(f.get_next_vertex(9), 3);
	BOOST_CHECK_EQUAL(f.get_next_vertex(3), 18);
	BOOST_CHECK_EQUAL(f.get_next_vertex(18), 9);
}

BOOST_AUTO_TEST_CASE(face_other_vertex_test) {
	Face f(6, 2, 5);
	// throw on illegal index?
	BOOST_CHECK_THROW(f.get_other_vertex(6, 20), std::runtime_error);
	BOOST_CHECK_THROW(f.get_other_vertex(60, 2), std::runtime_error);
	BOOST_CHECK_THROW(f.get_other_vertex(60, 20), std::runtime_error);
	// correct other vertex?
	BOOST_CHECK_EQUAL(f.get_other_vertex(6, 2), 5);
	BOOST_CHECK_EQUAL(f.get_other_vertex(2, 6), 5);
	BOOST_CHECK_EQUAL(f.get_other_vertex(2, 5), 6);
	BOOST_CHECK_EQUAL(f.get_other_vertex(5, 2), 6);
	BOOST_CHECK_EQUAL(f.get_other_vertex(6, 5), 2);
	BOOST_CHECK_EQUAL(f.get_other_vertex(5, 6), 2);
}

BOOST_AUTO_TEST_CASE(face_vertex_order_test_0) {
	Face f(6, 2, 5);
	BOOST_CHECK_EQUAL(f.v0, 2);
	BOOST_CHECK_EQUAL(f.v1, 5);
	BOOST_CHECK_EQUAL(f.v2, 6);
}

BOOST_AUTO_TEST_CASE(face_vertex_order_test_1) {
	Face f(2, 5, 6);
	BOOST_CHECK_EQUAL(f.v0, 2);
	BOOST_CHECK_EQUAL(f.v1, 5);
	BOOST_CHECK_EQUAL(f.v2, 6);
}

BOOST_AUTO_TEST_CASE(face_vertex_order_test_2) {
	Face f(5, 6, 2);
	BOOST_CHECK_EQUAL(f.v0, 2);
	BOOST_CHECK_EQUAL(f.v1, 5);
	BOOST_CHECK_EQUAL(f.v2, 6);
}

BOOST_AUTO_TEST_CASE(add_face_test) {
	Mesh m;

	// add faces
	BOOST_CHECK_NO_THROW(m.add_face(0, 1, 2));
	BOOST_CHECK_NO_THROW(m.add_face(2, 1, 3));
	BOOST_CHECK_NO_THROW(m.add_face(2, 3, 4));
	BOOST_CHECK_EQUAL(m.faces.size(), 3);
	BOOST_CHECK_EQUAL(m.edges.size(), 7);
	// add duplicate face
	BOOST_CHECK_NO_THROW(m.add_face(2, 3, 4));
	boost::shared_ptr<MFace> f0 = m.add_face(10, 11, 12);
	boost::shared_ptr<MFace> f1 = m.add_face(12, 10, 11);
	boost::shared_ptr<MFace> f2 = m.add_face(11, 12, 10);
	BOOST_CHECK_EQUAL(f0, f1);
	BOOST_CHECK_EQUAL(f0, f2);
	// one extra face, three extra edges
	BOOST_CHECK_EQUAL(m.faces.size(), 4);
	BOOST_CHECK_EQUAL(m.edges.size(), 10);
}

BOOST_AUTO_TEST_CASE(face_get_edge_test) {
	// construct mesh
	Mesh m;
	boost::shared_ptr<MFace> f0 = m.add_face(0, 1, 2);
	boost::shared_ptr<MFace> f1 = m.add_face(2, 1, 3);
	boost::shared_ptr<MFace> f2 = m.add_face(2, 3, 4);
	// throw on illegal index?
	BOOST_CHECK_THROW(f0->get_edge(0, 3), std::runtime_error);
	BOOST_CHECK_THROW(f0->get_edge(3, 0), std::runtime_error);
	BOOST_CHECK_THROW(f0->get_edge(3, 4), std::runtime_error);
	// correct edge?
	boost::shared_ptr<const MEdge> e0, e1;
	e0 = f0->get_edge(0, 1);
	e1 = f0->get_edge(1, 0);
	BOOST_CHECK_EQUAL(e0, e1);
	e0 = f0->get_edge(0, 1);
	e1 = f0->get_edge(1, 2);
	BOOST_CHECK(e0 != e1);
	e0 = f0->get_edge(1, 2);
	e1 = f1->get_edge(1, 2);
	BOOST_CHECK_EQUAL(e0, e1);
	BOOST_CHECK_EQUAL(e0->ev0, 1);
	BOOST_CHECK_EQUAL(e0->ev1, 2);
	BOOST_CHECK_EQUAL(e1->ev0, 1);
	BOOST_CHECK_EQUAL(e1->ev1, 2);
	e0 = f1->get_edge(2, 3);
	e1 = f2->get_edge(2, 3);
	BOOST_CHECK_EQUAL(e0, e1);
	BOOST_CHECK_EQUAL(e0->ev0, 2);
	BOOST_CHECK_EQUAL(e0->ev1, 3);
	BOOST_CHECK_EQUAL(e1->ev0, 2);
	BOOST_CHECK_EQUAL(e1->ev1, 3);
}

BOOST_AUTO_TEST_CASE(face_get_common_edges_test) {
	// construct mesh
	Mesh m;
	boost::shared_ptr<MFace> f0;
	boost::shared_ptr<MFace> f1;
	boost::shared_ptr<MFace> f2;
	f0 = m.add_face(0, 1, 2);
	f1 = m.add_face(2, 1, 3);
	f2 = m.add_face(2, 3, 4);
	// correct edge?
	std::vector<boost::shared_ptr<const MEdge> > common;
	common = f0->get_common_edges(*f2);
	BOOST_CHECK_EQUAL(common.size(), 0);
	common = f0->get_common_edges(*f1);
	BOOST_CHECK_EQUAL(common.size(), 1);
	BOOST_CHECK_EQUAL(common[0]->ev0, 1);
	BOOST_CHECK_EQUAL(common[0]->ev1, 2);
	common = f0->get_common_edges(*f0);
	BOOST_CHECK_EQUAL(common.size(), 3);
	BOOST_CHECK_EQUAL(common[0]->ev0, 0);
	BOOST_CHECK_EQUAL(common[0]->ev1, 1);
	BOOST_CHECK_EQUAL(common[1]->ev0, 1);
	BOOST_CHECK_EQUAL(common[1]->ev1, 2);
	BOOST_CHECK_EQUAL(common[2]->ev0, 0);
	BOOST_CHECK_EQUAL(common[2]->ev1, 2);
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>#include "terminal.h"
#include <iostream>
#include "logging.h"
#include "config.h"
#ifdef JUCI_ENABLE_DEBUG
#include "debug.h"
#endif

Terminal::InProgress::InProgress(const std::string& start_msg): stop(false) {
  waiting_print.connect([this](){
    Terminal::get().async_print(line_nr-1, ".");
  });
  start(start_msg);
}

Terminal::InProgress::~InProgress() {
  stop=true;
  if(wait_thread.joinable())
    wait_thread.join();
}

void Terminal::InProgress::start(const std::string& msg) {
  line_nr=Terminal::get().print(msg+"...\n");
  wait_thread=std::thread([this](){
    size_t c=0;
    while(!stop) {
      if(c%100==0)
        waiting_print();
      std::this_thread::sleep_for(std::chrono::milliseconds(10));
      c++;
    }
  });
}

void Terminal::InProgress::done(const std::string& msg) {
  if(!stop) {
    stop=true;
    Terminal::get().async_print(line_nr-1, msg);
  }
}

void Terminal::InProgress::cancel(const std::string& msg) {
  if(!stop) {
    stop=true;
    Terminal::get().async_print(line_nr-1, msg);
  }
}

Terminal::Terminal() {
  bold_tag=get_buffer()->create_tag();
  bold_tag->property_weight()=PANGO_WEIGHT_BOLD;

  async_print_dispatcher.connect([this](){
    async_print_strings_mutex.lock();
    if(async_print_strings.size()>0) {
      for(auto &string_bold: async_print_strings)
        print(string_bold.first, string_bold.second);
      async_print_strings.clear();
    }
    async_print_strings_mutex.unlock();
  });
  async_print_on_line_dispatcher.connect([this](){
    async_print_on_line_strings_mutex.lock();
    if(async_print_on_line_strings.size()>0) {
      for(auto &line_string: async_print_on_line_strings)
        print(line_string.first, line_string.second);
      async_print_on_line_strings.clear();
    }
    async_print_on_line_strings_mutex.unlock();
  });
}

int Terminal::process(const std::string &command, const boost::filesystem::path &path, bool use_pipes) {
  std::unique_ptr<Process> process;
  if(use_pipes)
    process=std::unique_ptr<Process>(new Process(command, path.string(), [this](const char* bytes, size_t n) {
      async_print(std::string(bytes, n));
    }, [this](const char* bytes, size_t n) {
      async_print(std::string(bytes, n), true);
    }));
  else
    process=std::unique_ptr<Process>(new Process(command, path.string()));

  if(process->get_id()<=0) {
    async_print("Error: failed to run command: " + command + "\n", true);
    return -1;
  }

  return process->get_exit_status();
}

int Terminal::process(std::istream &stdin_stream, std::ostream &stdout_stream, const std::string &command, const boost::filesystem::path &path) {
  Process process(command, path.string(), [this, &stdout_stream](const char* bytes, size_t n) {
    Glib::ustring umessage(std::string(bytes, n));
    Glib::ustring::iterator iter;
    while(!umessage.validate(iter)) {
      auto next_char_iter=iter;
      next_char_iter++;
      umessage.replace(iter, next_char_iter, "?");
    }
    stdout_stream.write(umessage.data(), n);
  }, [this](const char* bytes, size_t n) {
    async_print(std::string(bytes, n), true);
  }, true);

  if(process.get_id()<=0) {
    async_print("Error: failed to run command: " + command + "\n", true);
    return -1;
  }

  char buffer[131072];
  for(;;) {
    stdin_stream.readsome(buffer, 131072);
    auto read_n=stdin_stream.gcount();
    if(read_n==0)
      break;
    if(!process.write(buffer, read_n)) {
      break;
    }
  }
  process.close_stdin();

  return process.get_exit_status();
}

void Terminal::async_process(const std::string &command, const boost::filesystem::path &path, std::function<void(int exit_status)> callback) {
  std::thread async_execute_thread([this, command, path, callback](){
    processes_mutex.lock();
    stdin_buffer.clear();
    std::shared_ptr<Process> process(new Process(command, path.string(), [this](const char* bytes, size_t n) {
      async_print(std::string(bytes, n));
    }, [this](const char* bytes, size_t n) {
      async_print(std::string(bytes, n), true);
    }, true));
    auto pid=process->get_id();
    if (pid<=0) {
      processes_mutex.unlock();
      async_print("Error: failed to run command: " + command + "\n", true);
      if(callback)
        callback(-1);
      return;
    }
    else {
      processes.emplace_back(process);
      processes_mutex.unlock();
    }

    auto exit_status=process->get_exit_status();

    processes_mutex.lock();
    for(auto it=processes.begin();it!=processes.end();it++) {
      if((*it)->get_id()==pid) {
        processes.erase(it);
        break;
      }
    }
    stdin_buffer.clear();
    processes_mutex.unlock();

    if(callback)
      callback(exit_status);
  });
  async_execute_thread.detach();
}

void Terminal::kill_last_async_process(bool force) {
  processes_mutex.lock();
  if(processes.size()>0)
    processes.back()->kill(force);
  processes_mutex.unlock();
}

void Terminal::kill_async_processes(bool force) {
  processes_mutex.lock();
  for(auto &process: processes)
    process->kill(force);
  processes_mutex.unlock();
}

size_t Terminal::print(const std::string &message, bool bold){
  Glib::ustring umessage=message;
  Glib::ustring::iterator iter;
  while(!umessage.validate(iter)) {
    auto next_char_iter=iter;
    next_char_iter++;
    umessage.replace(iter, next_char_iter, "?");
  }

  if(bold)
    get_buffer()->insert_with_tag(get_buffer()->end(), umessage, bold_tag);
  else
    get_buffer()->insert(get_buffer()->end(), umessage);

  if(get_buffer()->get_line_count()>Config::get().terminal.history_size) {
    int lines=get_buffer()->get_line_count()-Config::get().terminal.history_size;
    get_buffer()->erase(get_buffer()->begin(), get_buffer()->get_iter_at_line(lines));
    deleted_lines+=static_cast<size_t>(lines);
  }

  return static_cast<size_t>(get_buffer()->end().get_line())+deleted_lines;
}

void Terminal::print(size_t line_nr, const std::string &message){
  if(line_nr<deleted_lines)
    return;

  Glib::ustring umessage=message;
  Glib::ustring::iterator iter;
  while(!umessage.validate(iter)) {
    auto next_char_iter=iter;
    next_char_iter++;
    umessage.replace(iter, next_char_iter, "?");
  }

  auto end_line_iter=get_buffer()->get_iter_at_line(static_cast<int>(line_nr-deleted_lines));
  while(!end_line_iter.ends_line() && end_line_iter.forward_char()) {}
  get_buffer()->insert(end_line_iter, umessage);
}

std::shared_ptr<Terminal::InProgress> Terminal::print_in_progress(std::string start_msg) {
  std::shared_ptr<Terminal::InProgress> in_progress=std::shared_ptr<Terminal::InProgress>(new Terminal::InProgress(start_msg));
  return in_progress;
}

void Terminal::async_print(const std::string &message, bool bold) {
  async_print_strings_mutex.lock();
  bool dispatch=true;
  if(async_print_strings.size()>0)
    dispatch=false;
  async_print_strings.emplace_back(message, bold);
  async_print_strings_mutex.unlock();
  if(dispatch)
    async_print_dispatcher();
}

void Terminal::async_print(int line_nr, const std::string &message) {
  async_print_on_line_strings_mutex.lock();
  bool dispatch=true;
  if(async_print_on_line_strings.size()>0)
    dispatch=false;
  async_print_on_line_strings.emplace_back(line_nr, message);
  async_print_on_line_strings_mutex.unlock();
  if(dispatch)
    async_print_on_line_dispatcher();
}

bool Terminal::on_key_press_event(GdkEventKey *event) {
  processes_mutex.lock();
  bool debug_is_running=false;
#ifdef JUCI_ENABLE_DEBUG
  debug_is_running=Debug::get().is_running();
#endif
  if(processes.size()>0 || debug_is_running) {
    get_buffer()->place_cursor(get_buffer()->end());
    auto unicode=gdk_keyval_to_unicode(event->keyval);
    char chr=(char)unicode;
    if(unicode>=32 && unicode<=126) {
      stdin_buffer+=chr;
      get_buffer()->insert_at_cursor(stdin_buffer.substr(stdin_buffer.size()-1));
    }
    else if(event->keyval==GDK_KEY_BackSpace) {
      if(stdin_buffer.size()>0 && get_buffer()->get_char_count()>0) {
        auto iter=get_buffer()->end();
        iter--;
        stdin_buffer.pop_back();
        get_buffer()->erase(iter, get_buffer()->end());
      }
    }
    else if(event->keyval==GDK_KEY_Return) {
      stdin_buffer+='\n';
      if(debug_is_running) {
#ifdef JUCI_ENABLE_DEBUG
        Debug::get().write(stdin_buffer);
#endif
      }
      else
        processes.back()->write(stdin_buffer);
      get_buffer()->insert_at_cursor(stdin_buffer.substr(stdin_buffer.size()-1));
      stdin_buffer.clear();
    }
  }
  processes_mutex.unlock();
  return true;
}
<commit_msg>Removed color codes from terminal output in Windows<commit_after>#include "terminal.h"
#include <iostream>
#include "logging.h"
#include "config.h"
#ifdef JUCI_ENABLE_DEBUG
#include "debug.h"
#endif

Terminal::InProgress::InProgress(const std::string& start_msg): stop(false) {
  waiting_print.connect([this](){
    Terminal::get().async_print(line_nr-1, ".");
  });
  start(start_msg);
}

Terminal::InProgress::~InProgress() {
  stop=true;
  if(wait_thread.joinable())
    wait_thread.join();
}

void Terminal::InProgress::start(const std::string& msg) {
  line_nr=Terminal::get().print(msg+"...\n");
  wait_thread=std::thread([this](){
    size_t c=0;
    while(!stop) {
      if(c%100==0)
        waiting_print();
      std::this_thread::sleep_for(std::chrono::milliseconds(10));
      c++;
    }
  });
}

void Terminal::InProgress::done(const std::string& msg) {
  if(!stop) {
    stop=true;
    Terminal::get().async_print(line_nr-1, msg);
  }
}

void Terminal::InProgress::cancel(const std::string& msg) {
  if(!stop) {
    stop=true;
    Terminal::get().async_print(line_nr-1, msg);
  }
}

Terminal::Terminal() {
  bold_tag=get_buffer()->create_tag();
  bold_tag->property_weight()=PANGO_WEIGHT_BOLD;

  async_print_dispatcher.connect([this](){
    async_print_strings_mutex.lock();
    if(async_print_strings.size()>0) {
      for(auto &string_bold: async_print_strings)
        print(string_bold.first, string_bold.second);
      async_print_strings.clear();
    }
    async_print_strings_mutex.unlock();
  });
  async_print_on_line_dispatcher.connect([this](){
    async_print_on_line_strings_mutex.lock();
    if(async_print_on_line_strings.size()>0) {
      for(auto &line_string: async_print_on_line_strings)
        print(line_string.first, line_string.second);
      async_print_on_line_strings.clear();
    }
    async_print_on_line_strings_mutex.unlock();
  });
}

int Terminal::process(const std::string &command, const boost::filesystem::path &path, bool use_pipes) {
  std::unique_ptr<Process> process;
  if(use_pipes)
    process=std::unique_ptr<Process>(new Process(command, path.string(), [this](const char* bytes, size_t n) {
      async_print(std::string(bytes, n));
    }, [this](const char* bytes, size_t n) {
      async_print(std::string(bytes, n), true);
    }));
  else
    process=std::unique_ptr<Process>(new Process(command, path.string()));

  if(process->get_id()<=0) {
    async_print("Error: failed to run command: " + command + "\n", true);
    return -1;
  }

  return process->get_exit_status();
}

int Terminal::process(std::istream &stdin_stream, std::ostream &stdout_stream, const std::string &command, const boost::filesystem::path &path) {
  Process process(command, path.string(), [this, &stdout_stream](const char* bytes, size_t n) {
    Glib::ustring umessage(std::string(bytes, n));
    Glib::ustring::iterator iter;
    while(!umessage.validate(iter)) {
      auto next_char_iter=iter;
      next_char_iter++;
      umessage.replace(iter, next_char_iter, "?");
    }
    stdout_stream.write(umessage.data(), n);
  }, [this](const char* bytes, size_t n) {
    async_print(std::string(bytes, n), true);
  }, true);

  if(process.get_id()<=0) {
    async_print("Error: failed to run command: " + command + "\n", true);
    return -1;
  }

  char buffer[131072];
  for(;;) {
    stdin_stream.readsome(buffer, 131072);
    auto read_n=stdin_stream.gcount();
    if(read_n==0)
      break;
    if(!process.write(buffer, read_n)) {
      break;
    }
  }
  process.close_stdin();

  return process.get_exit_status();
}

void Terminal::async_process(const std::string &command, const boost::filesystem::path &path, std::function<void(int exit_status)> callback) {
  std::thread async_execute_thread([this, command, path, callback](){
    processes_mutex.lock();
    stdin_buffer.clear();
    std::shared_ptr<Process> process(new Process(command, path.string(), [this](const char* bytes, size_t n) {
      async_print(std::string(bytes, n));
    }, [this](const char* bytes, size_t n) {
      async_print(std::string(bytes, n), true);
    }, true));
    auto pid=process->get_id();
    if (pid<=0) {
      processes_mutex.unlock();
      async_print("Error: failed to run command: " + command + "\n", true);
      if(callback)
        callback(-1);
      return;
    }
    else {
      processes.emplace_back(process);
      processes_mutex.unlock();
    }

    auto exit_status=process->get_exit_status();

    processes_mutex.lock();
    for(auto it=processes.begin();it!=processes.end();it++) {
      if((*it)->get_id()==pid) {
        processes.erase(it);
        break;
      }
    }
    stdin_buffer.clear();
    processes_mutex.unlock();

    if(callback)
      callback(exit_status);
  });
  async_execute_thread.detach();
}

void Terminal::kill_last_async_process(bool force) {
  processes_mutex.lock();
  if(processes.size()>0)
    processes.back()->kill(force);
  processes_mutex.unlock();
}

void Terminal::kill_async_processes(bool force) {
  processes_mutex.lock();
  for(auto &process: processes)
    process->kill(force);
  processes_mutex.unlock();
}

size_t Terminal::print(const std::string &message, bool bold){
#ifdef _WIN32
  //Remove color codes
  auto message_no_color=message; //copy here since operations on Glib::ustring is too slow
  size_t pos=0;
  while((pos=message_no_color.find('\e', pos))!=std::string::npos) {
    if((pos+2)>=message_no_color.size())
      break;
    if(message_no_color[pos+1]=='[') {
      size_t end_pos=pos+2;
      bool color_code_found=false;
      while(end_pos<message_no_color.size()) {
        if((message_no_color[end_pos]>='0' && message_no_color[end_pos]<='9') || message_no_color[end_pos]==';')
          end_pos++;
        else if(message_no_color[end_pos]=='m') {
          color_code_found=true;
          break;
        }
        else
          break;
      }
      if(color_code_found)
        message_no_color.erase(pos, end_pos-pos+1);
    }
  }
  Glib::ustring umessage=message_no_color;
#else
  Glib::ustring umessage=message;
#endif

  Glib::ustring::iterator iter;
  while(!umessage.validate(iter)) {
    auto next_char_iter=iter;
    next_char_iter++;
    umessage.replace(iter, next_char_iter, "?");
  }

  if(bold)
    get_buffer()->insert_with_tag(get_buffer()->end(), umessage, bold_tag);
  else
    get_buffer()->insert(get_buffer()->end(), umessage);

  if(get_buffer()->get_line_count()>Config::get().terminal.history_size) {
    int lines=get_buffer()->get_line_count()-Config::get().terminal.history_size;
    get_buffer()->erase(get_buffer()->begin(), get_buffer()->get_iter_at_line(lines));
    deleted_lines+=static_cast<size_t>(lines);
  }

  return static_cast<size_t>(get_buffer()->end().get_line())+deleted_lines;
}

void Terminal::print(size_t line_nr, const std::string &message){
  if(line_nr<deleted_lines)
    return;

  Glib::ustring umessage=message;
  Glib::ustring::iterator iter;
  while(!umessage.validate(iter)) {
    auto next_char_iter=iter;
    next_char_iter++;
    umessage.replace(iter, next_char_iter, "?");
  }

  auto end_line_iter=get_buffer()->get_iter_at_line(static_cast<int>(line_nr-deleted_lines));
  while(!end_line_iter.ends_line() && end_line_iter.forward_char()) {}
  get_buffer()->insert(end_line_iter, umessage);
}

std::shared_ptr<Terminal::InProgress> Terminal::print_in_progress(std::string start_msg) {
  std::shared_ptr<Terminal::InProgress> in_progress=std::shared_ptr<Terminal::InProgress>(new Terminal::InProgress(start_msg));
  return in_progress;
}

void Terminal::async_print(const std::string &message, bool bold) {
  async_print_strings_mutex.lock();
  bool dispatch=true;
  if(async_print_strings.size()>0)
    dispatch=false;
  async_print_strings.emplace_back(message, bold);
  async_print_strings_mutex.unlock();
  if(dispatch)
    async_print_dispatcher();
}

void Terminal::async_print(int line_nr, const std::string &message) {
  async_print_on_line_strings_mutex.lock();
  bool dispatch=true;
  if(async_print_on_line_strings.size()>0)
    dispatch=false;
  async_print_on_line_strings.emplace_back(line_nr, message);
  async_print_on_line_strings_mutex.unlock();
  if(dispatch)
    async_print_on_line_dispatcher();
}

bool Terminal::on_key_press_event(GdkEventKey *event) {
  processes_mutex.lock();
  bool debug_is_running=false;
#ifdef JUCI_ENABLE_DEBUG
  debug_is_running=Debug::get().is_running();
#endif
  if(processes.size()>0 || debug_is_running) {
    get_buffer()->place_cursor(get_buffer()->end());
    auto unicode=gdk_keyval_to_unicode(event->keyval);
    char chr=(char)unicode;
    if(unicode>=32 && unicode<=126) {
      stdin_buffer+=chr;
      get_buffer()->insert_at_cursor(stdin_buffer.substr(stdin_buffer.size()-1));
    }
    else if(event->keyval==GDK_KEY_BackSpace) {
      if(stdin_buffer.size()>0 && get_buffer()->get_char_count()>0) {
        auto iter=get_buffer()->end();
        iter--;
        stdin_buffer.pop_back();
        get_buffer()->erase(iter, get_buffer()->end());
      }
    }
    else if(event->keyval==GDK_KEY_Return) {
      stdin_buffer+='\n';
      if(debug_is_running) {
#ifdef JUCI_ENABLE_DEBUG
        Debug::get().write(stdin_buffer);
#endif
      }
      else
        processes.back()->write(stdin_buffer);
      get_buffer()->insert_at_cursor(stdin_buffer.substr(stdin_buffer.size()-1));
      stdin_buffer.clear();
    }
  }
  processes_mutex.unlock();
  return true;
}
<|endoftext|>


<commit_before>#include "test.h"
#include "event_fixture.h"

#include "vast/object.h"
#include "vast/serialization.h"
#include "vast/io/serialization.h"

using namespace vast;

BOOST_FIXTURE_TEST_SUITE(event_serialization_tests, event_fixture)

BOOST_AUTO_TEST_CASE(byte_swapping)
{
  using util::byte_swap;

  uint8_t  x08 = 0x11;
  uint16_t x16 = 0x1122;
  uint32_t x32 = 0x11223344;
  uint64_t x64 = 0x1122334455667788;
  // TODO: extend to all arithmetic types, e.g., float and double.

  auto y08 = byte_swap<little_endian, big_endian>(x08);
  auto y16 = byte_swap<little_endian, big_endian>(x16);
  auto y32 = byte_swap<little_endian, big_endian>(x32);
  auto y64 = byte_swap<little_endian, big_endian>(x64);
  BOOST_CHECK_EQUAL(y08, 0x11);
  BOOST_CHECK_EQUAL(y16, 0x2211);
  BOOST_CHECK_EQUAL(y32, 0x44332211);
  BOOST_CHECK_EQUAL(y64, 0x8877665544332211);

  y08 = byte_swap<big_endian, little_endian>(y08);
  y16 = byte_swap<big_endian, little_endian>(y16);
  y32 = byte_swap<big_endian, little_endian>(y32);
  y64 = byte_swap<big_endian, little_endian>(y64);
  BOOST_CHECK_EQUAL(y08, x08);
  BOOST_CHECK_EQUAL(y16, x16);
  BOOST_CHECK_EQUAL(y32, x32);
  BOOST_CHECK_EQUAL(y64, x64);

  // NOP
  y08 = byte_swap<big_endian, big_endian>(y08);
  y16 = byte_swap<big_endian, big_endian>(y16);
  y32 = byte_swap<big_endian, big_endian>(y32);
  y64 = byte_swap<big_endian, big_endian>(y64);
  BOOST_CHECK_EQUAL(y08, x08);
  BOOST_CHECK_EQUAL(y16, x16);
  BOOST_CHECK_EQUAL(y32, x32);
  BOOST_CHECK_EQUAL(y64, x64);

  // NOP
  y08 = byte_swap<little_endian, little_endian>(y08);
  y16 = byte_swap<little_endian, little_endian>(y16);
  y32 = byte_swap<little_endian, little_endian>(y32);
  y64 = byte_swap<little_endian, little_endian>(y64);
  BOOST_CHECK_EQUAL(y08, x08);
  BOOST_CHECK_EQUAL(y16, x16);
  BOOST_CHECK_EQUAL(y32, x32);
  BOOST_CHECK_EQUAL(y64, x64);
}

BOOST_AUTO_TEST_CASE(containers)
{
  std::vector<double> v0{4.2, 8.4, 16.8}, v1;
  std::list<int> l0{4, 2}, l1;
  std::unordered_map<int, int> u0{{4, 2}, {8, 4}}, u1;

  std::vector<uint8_t> buf;
  io::archive(buf, v0, l0, u0);
  io::unarchive(buf, v1, l1, u1);

  BOOST_CHECK(v0 == v1);
  BOOST_CHECK(l0 == l1);
  BOOST_CHECK(u0 == u1);
}

// A serializable class.
class serializable
{
  friend struct access;
public:
  int i() const
  {
    return i_;
  }

private:
  void serialize(serializer& sink) const
  {
    sink << i_ - 10;
  }

  void deserialize(deserializer& source)
  {
    source >> i_;
    i_ += 10;
  }

  int i_ = 42;
};

BOOST_AUTO_TEST_CASE(io_serialization_interface)
{
  std::vector<io::compression> methods{io::null, io::lz4};
#ifdef VAST_HAVE_SNAPPY
  methods.push_back(io::snappy);
#endif // VAST_HAVE_SNAPPY
  for (auto method : methods)
  {
    std::vector<int> input(1u << 10), output;
    BOOST_REQUIRE(input.size() % 2 == 0);
    for (size_t i = 0; i < input.size() / 2; ++i)
      input[i] = i % 128;
    for (size_t i = input.size() / 2; i < input.size(); ++i)
      input[i] = i % 2;

    std::vector<uint8_t> buf;
    serializable x;
    io::compress(method, buf, input, serializable());
    io::decompress(method, buf, output, x);

    BOOST_REQUIRE_EQUAL(input.size(), output.size());
    for (size_t i = 0; i < input.size(); ++i)
      BOOST_CHECK_EQUAL(output[i], input[i]);
    BOOST_CHECK_EQUAL(x.i(), 42);
  }
}

BOOST_AUTO_TEST_CASE(object_serialization)
{
  object o, p;
  o = object::adopt(new record{42, 84, 1337});

  std::vector<uint8_t> buf;
  io::archive(buf, o);
  io::unarchive(buf, p);

  BOOST_CHECK(o == p);
}

struct base
{
  virtual ~base() = default;
  virtual uint32_t f() const = 0;
  virtual void serialize(serializer& sink) const = 0;
  virtual void deserialize(deserializer& source) = 0;
};

struct derived : base
{
  friend bool operator==(derived const& x, derived const& y)
  {
    return x.i == y.i;
  }

  virtual uint32_t f() const final
  {
    return i;
  }

  virtual void serialize(serializer& sink) const final
  {
    sink << i;
  }

  virtual void deserialize(deserializer& source) final
  {
    source >> i;
  }

  uint32_t i = 0;
};

BOOST_AUTO_TEST_CASE(polymorphic_object_serialization)
{
  derived d;
  d.i = 42;

  // Polymorphic types must be announced as their concrete type is not known at
  // compile time.
  BOOST_REQUIRE((announce<derived>()));

  // Due to the lacking introspection capabilities in C++, the serialization
  // framework requires explicit registration of each derived
  // class to provide type-safe access.
  BOOST_REQUIRE((make_convertible<derived, base>()));

  std::vector<uint8_t> buf;
  {
    // We serialize the object through a polymorphic reference to the base
    // class, which simply invokes the correct virtual serialize() method of
    // the derived class.
    auto out = io::make_container_output_stream(buf);
    binary_serializer sink(out);
    base& b = d;
    BOOST_REQUIRE(write_object(sink, b));
  }
  {
    // Actually, this is the same as serializing through a pointer directory,
    // because serializing a pointer assumes reference semantics and hence
    // writes an instance out as object.
    decltype(buf) buf2;
    base* bp = &d;
    io::archive(buf2, bp);
    BOOST_CHECK_EQUAL_COLLECTIONS(buf.begin(), buf.end(),
                                  buf2.begin(), buf2.end());
  }
  {
    // It should always be possible to deserialize an instance of the exact
    // derived type. This technically does not require any virtual functions.
    auto in = io::make_array_input_stream(buf);
    binary_deserializer source(in);
    derived e;
    BOOST_REQUIRE(read_object(source, e));
    BOOST_CHECK_EQUAL(e.i, 42);
  }
  {
    // Similarly, it should always be possible to retrieve an opaque object and
    // get the derived type via a type-checked invocation of get<T>.
    object o;
    io::unarchive(buf, o);
    BOOST_CHECK(o.convertible_to<derived>());
    BOOST_CHECK_EQUAL(get<derived>(o).f(), 42);
    // Since we've announced the type as being convertible to its base class,
    // we can also safely obtain a reference to the base.
    BOOST_CHECK(o.convertible_to<base>());
    BOOST_CHECK_EQUAL(get<base>(o).f(), 42);
    // Moreover, since we've assurred convertability we can extract the raw
    // instance from the object. Thereafter, we own it and have to delete it.
    base* b = o.release_as<base>();
    BOOST_CHECK_EQUAL(b->f(), 42);
    BOOST_REQUIRE(b != nullptr);
    delete b;
  }
  {
    // Finally, since all serializations of pointers are assumed to pertain to
    // objects with reference semantics, we can just serialize the pointer
    // directly.
    base* b = nullptr;
    io::unarchive(buf, b);
    BOOST_REQUIRE(b != nullptr);
    BOOST_CHECK_EQUAL(b->f(), 42);
    delete b;
  }
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>Fix issue with ambiguous befriending.<commit_after>#include "test.h"
#include "event_fixture.h"

#include "vast/object.h"
#include "vast/serialization.h"
#include "vast/io/serialization.h"

using namespace vast;

BOOST_FIXTURE_TEST_SUITE(event_serialization_tests, event_fixture)

BOOST_AUTO_TEST_CASE(byte_swapping)
{
  using util::byte_swap;

  uint8_t  x08 = 0x11;
  uint16_t x16 = 0x1122;
  uint32_t x32 = 0x11223344;
  uint64_t x64 = 0x1122334455667788;
  // TODO: extend to all arithmetic types, e.g., float and double.

  auto y08 = byte_swap<little_endian, big_endian>(x08);
  auto y16 = byte_swap<little_endian, big_endian>(x16);
  auto y32 = byte_swap<little_endian, big_endian>(x32);
  auto y64 = byte_swap<little_endian, big_endian>(x64);
  BOOST_CHECK_EQUAL(y08, 0x11);
  BOOST_CHECK_EQUAL(y16, 0x2211);
  BOOST_CHECK_EQUAL(y32, 0x44332211);
  BOOST_CHECK_EQUAL(y64, 0x8877665544332211);

  y08 = byte_swap<big_endian, little_endian>(y08);
  y16 = byte_swap<big_endian, little_endian>(y16);
  y32 = byte_swap<big_endian, little_endian>(y32);
  y64 = byte_swap<big_endian, little_endian>(y64);
  BOOST_CHECK_EQUAL(y08, x08);
  BOOST_CHECK_EQUAL(y16, x16);
  BOOST_CHECK_EQUAL(y32, x32);
  BOOST_CHECK_EQUAL(y64, x64);

  // NOP
  y08 = byte_swap<big_endian, big_endian>(y08);
  y16 = byte_swap<big_endian, big_endian>(y16);
  y32 = byte_swap<big_endian, big_endian>(y32);
  y64 = byte_swap<big_endian, big_endian>(y64);
  BOOST_CHECK_EQUAL(y08, x08);
  BOOST_CHECK_EQUAL(y16, x16);
  BOOST_CHECK_EQUAL(y32, x32);
  BOOST_CHECK_EQUAL(y64, x64);

  // NOP
  y08 = byte_swap<little_endian, little_endian>(y08);
  y16 = byte_swap<little_endian, little_endian>(y16);
  y32 = byte_swap<little_endian, little_endian>(y32);
  y64 = byte_swap<little_endian, little_endian>(y64);
  BOOST_CHECK_EQUAL(y08, x08);
  BOOST_CHECK_EQUAL(y16, x16);
  BOOST_CHECK_EQUAL(y32, x32);
  BOOST_CHECK_EQUAL(y64, x64);
}

BOOST_AUTO_TEST_CASE(containers)
{
  std::vector<double> v0{4.2, 8.4, 16.8}, v1;
  std::list<int> l0{4, 2}, l1;
  std::unordered_map<int, int> u0{{4, 2}, {8, 4}}, u1;

  std::vector<uint8_t> buf;
  io::archive(buf, v0, l0, u0);
  io::unarchive(buf, v1, l1, u1);

  BOOST_CHECK(v0 == v1);
  BOOST_CHECK(l0 == l1);
  BOOST_CHECK(u0 == u1);
}

// A serializable class.
class serializable
{
public:
  int i() const
  {
    return i_;
  }

private:
  friend vast::access;

  void serialize(serializer& sink) const
  {
    sink << i_ - 10;
  }

  void deserialize(deserializer& source)
  {
    source >> i_;
    i_ += 10;
  }

  int i_ = 42;
};

BOOST_AUTO_TEST_CASE(io_serialization_interface)
{
  std::vector<io::compression> methods{io::null, io::lz4};
#ifdef VAST_HAVE_SNAPPY
  methods.push_back(io::snappy);
#endif // VAST_HAVE_SNAPPY
  for (auto method : methods)
  {
    std::vector<int> input(1u << 10), output;
    BOOST_REQUIRE(input.size() % 2 == 0);
    for (size_t i = 0; i < input.size() / 2; ++i)
      input[i] = i % 128;
    for (size_t i = input.size() / 2; i < input.size(); ++i)
      input[i] = i % 2;

    std::vector<uint8_t> buf;
    serializable x;
    io::compress(method, buf, input, serializable());
    io::decompress(method, buf, output, x);

    BOOST_REQUIRE_EQUAL(input.size(), output.size());
    for (size_t i = 0; i < input.size(); ++i)
      BOOST_CHECK_EQUAL(output[i], input[i]);
    BOOST_CHECK_EQUAL(x.i(), 42);
  }
}

BOOST_AUTO_TEST_CASE(object_serialization)
{
  object o, p;
  o = object::adopt(new record{42, 84, 1337});

  std::vector<uint8_t> buf;
  io::archive(buf, o);
  io::unarchive(buf, p);

  BOOST_CHECK(o == p);
}

struct base
{
  virtual ~base() = default;
  virtual uint32_t f() const = 0;
  virtual void serialize(serializer& sink) const = 0;
  virtual void deserialize(deserializer& source) = 0;
};

struct derived : base
{
  friend bool operator==(derived const& x, derived const& y)
  {
    return x.i == y.i;
  }

  virtual uint32_t f() const final
  {
    return i;
  }

  virtual void serialize(serializer& sink) const final
  {
    sink << i;
  }

  virtual void deserialize(deserializer& source) final
  {
    source >> i;
  }

  uint32_t i = 0;
};

BOOST_AUTO_TEST_CASE(polymorphic_object_serialization)
{
  derived d;
  d.i = 42;

  // Polymorphic types must be announced as their concrete type is not known at
  // compile time.
  BOOST_REQUIRE((announce<derived>()));

  // Due to the lacking introspection capabilities in C++, the serialization
  // framework requires explicit registration of each derived
  // class to provide type-safe access.
  BOOST_REQUIRE((make_convertible<derived, base>()));

  std::vector<uint8_t> buf;
  {
    // We serialize the object through a polymorphic reference to the base
    // class, which simply invokes the correct virtual serialize() method of
    // the derived class.
    auto out = io::make_container_output_stream(buf);
    binary_serializer sink(out);
    base& b = d;
    BOOST_REQUIRE(write_object(sink, b));
  }
  {
    // Actually, this is the same as serializing through a pointer directory,
    // because serializing a pointer assumes reference semantics and hence
    // writes an instance out as object.
    decltype(buf) buf2;
    base* bp = &d;
    io::archive(buf2, bp);
    BOOST_CHECK_EQUAL_COLLECTIONS(buf.begin(), buf.end(),
                                  buf2.begin(), buf2.end());
  }
  {
    // It should always be possible to deserialize an instance of the exact
    // derived type. This technically does not require any virtual functions.
    auto in = io::make_array_input_stream(buf);
    binary_deserializer source(in);
    derived e;
    BOOST_REQUIRE(read_object(source, e));
    BOOST_CHECK_EQUAL(e.i, 42);
  }
  {
    // Similarly, it should always be possible to retrieve an opaque object and
    // get the derived type via a type-checked invocation of get<T>.
    object o;
    io::unarchive(buf, o);
    BOOST_CHECK(o.convertible_to<derived>());
    BOOST_CHECK_EQUAL(get<derived>(o).f(), 42);
    // Since we've announced the type as being convertible to its base class,
    // we can also safely obtain a reference to the base.
    BOOST_CHECK(o.convertible_to<base>());
    BOOST_CHECK_EQUAL(get<base>(o).f(), 42);
    // Moreover, since we've assurred convertability we can extract the raw
    // instance from the object. Thereafter, we own it and have to delete it.
    base* b = o.release_as<base>();
    BOOST_CHECK_EQUAL(b->f(), 42);
    BOOST_REQUIRE(b != nullptr);
    delete b;
  }
  {
    // Finally, since all serializations of pointers are assumed to pertain to
    // objects with reference semantics, we can just serialize the pointer
    // directly.
    base* b = nullptr;
    io::unarchive(buf, b);
    BOOST_REQUIRE(b != nullptr);
    BOOST_CHECK_EQUAL(b->f(), 42);
    delete b;
  }
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>/* IMPORTANT TODO: This assumes the identity is always the first element of
 * basic_group<T>::elements().  Make sure this always holds. */

#ifndef GROUP_H
#define GROUP_H

#define GROUP_CHECKS_MEMBERSHIP

#include <map>
#include <sstream>
#include <vector>
#include "Groups/BasicGroup.hpp"
#include "Groups/Element.hpp"
#include "Groups/util.hpp"

namespace Groups {
 template<> class basic_group<Element> {
 public:

  template<class T> basic_group(const basic_group<T>& g) {
   std::vector<T> gelems = g.elements();
   int qty = g.order();
   std::map<T,int> elemdex;
   for (int i=0; i<qty; i++) elemdex[gelems[i]] = i;
   table = std::vector< std::vector<int> >(qty, std::vector<int>(qty));
   for (int i=0; i<qty; i++) {
    for (int j=0; j<qty; j++) {
     table[i][j] = elemdex[g.oper(gelems[i], gelems[j])];
    }
   }
   inverses = std::vector<int>(qty);
   orders = std::vector<int>(qty);
   strs = std::vector<std::string>(qty);
   for (int i=0; i<qty; i++) {
    inverses[i] = elemdex[g.invert(gelems[i])];
    orders[i] = g.order(gelems[i]);
    strs[i] = g.showElem(gelems[i]);
   }
   abel = g.abelian();
  }

  basic_group(const basic_group<Element>& t)
   : table(t.table), inverses(t.inverses), orders(t.orders), strs(t.strs),
     abel(t.abel) { }

  virtual ~basic_group() { }

  virtual int oper(const int& x, const int& y) const {return table[x][y]; }

  virtual Element oper(const Element& x, const Element& y) const {
#ifdef GROUP_CHECKS_MEMBERSHIP
   if (!contains(x) || !contains(y)) throw group_mismatch("Group::oper");
#endif
   return Element(this, table[x.val][y.val]);
  }

  virtual Element identity() const {return Element(this, 0); }

  virtual std::vector<Element> elements() const {
   std::vector<Element> elems(table.size(), identity());
   std::vector<Element>::iterator iter = elems.begin();
   int i=0;
   for (iter++, i++; iter != elems.end(); iter++, i++) {
    *iter = Element(this, i);
   }
   return elems;
  }

  virtual int invert(const int& x) const {return inverses[x]; }

  virtual Element invert(const Element& x) const {
#ifdef GROUP_CHECKS_MEMBERSHIP
   if (!contains(x)) throw group_mismatch("Group::invert");
#endif
   return Element(this, inverses[x.val]);
  }

  virtual int order() const {return table.size(); }

  virtual int order(const int& x) const {return orders[x]; }

  virtual int order(const Element& x) const {
#ifdef GROUP_CHECKS_MEMBERSHIP
   if (!contains(x)) throw group_mismatch("Group::order");
#endif
   return orders[x.val];
  }

  virtual std::string showElem(const int& x) const {return strs[x]; }

  virtual std::string showElem(const Element& x) const {
#ifdef GROUP_CHECKS_MEMBERSHIP
   if (!contains(x)) throw group_mismatch("Group::showElem");
#endif
   return strs[x.val];
  }

  virtual bool abelian() const {return abel; }

  virtual basic_group<Element>* copy() const {
   return new basic_group<Element>(*this);
  }

  virtual int cmp(const basic_group<Element>* other) const {
   int ct = cmpTypes(*this, *other);
   if (ct != 0) return ct;
   const basic_group<Element>* c = static_cast<const basic_group<Element>*>(other);
   return table < c->table ? -1 : table > c->table ? 1 : 0;
   /* TODO: Should `strs` also take part in comparisons? */
  }

  virtual bool contains(const int& x) const {return 0 <= x && x < order(); }

  virtual bool contains(const Element& x) const {
   return x.gr->cmp(this) == 0 && 0 <= x.val && x.val < order();
  }

  basic_group<Element> direct(const basic_group<Element>& right) {
   int leftQty = order(), rightQty = right.order();
   int newQty = leftQty * rightQty;
   std::vector< std::vector<int> > tbl(newQty, std::vector<int>(newQty));
   for (int i=0; i<newQty; i++) {
    int xa = i / rightQty, xb = i % rightQty;
    for (int j=0; j<newQty; j++) {
     int ya = j / rightQty, yb = j % rightQty;
     tbl[i][j] = table[xa][ya] * rightQty + right.table[xb][yb];
    }
   }
   std::vector<int> invs = std::vector<int>(newQty);
   std::vector<int> ords = std::vector<int>(newQty);
   std::vector<std::string> ss = std::vector<std::string>(newQty);
   for (int i=0; i<newQty; i++) {
    int a = i / rightQty, b = i % rightQty;
    invs[i] = inverses[a] * rightQty + right.inverses[b];
    ords[i] = lcm(orders[a], right.orders[b]);
    if (i == 0) {
     ss[i] = "1";
    } else {
     std::ostringstream out;
     out << '(' << strs[a] << ", " << right.strs[b] << ')';
     ss[i] = out.str();
    }
   }
   return basic_group<Element>(tbl, invs, ords, ss, abel && right.abel);
  }

 private:
  basic_group(std::vector< std::vector<int> > tbl, std::vector<int> invs,
	      std::vector<int> ords, std::vector<std::string> ss, bool abelian)
   : table(tbl), inverses(invs), orders(ords), strs(ss), abel(abelian) { }

  std::vector< std::vector<int> > table;
  std::vector<int> inverses, orders;
  std::vector<std::string> strs;
  bool abel;
 };

 typedef basic_group<Element> Group;
}

#endif
<commit_msg>Forgot to implement Group::indexElem<commit_after>/* IMPORTANT TODO: This assumes the identity is always the first element of
 * basic_group<T>::elements().  Make sure this always holds. */

#ifndef GROUP_H
#define GROUP_H

#define GROUP_CHECKS_MEMBERSHIP

#include <map>
#include <sstream>
#include <vector>
#include "Groups/BasicGroup.hpp"
#include "Groups/Element.hpp"
#include "Groups/util.hpp"

namespace Groups {
 template<> class basic_group<Element> {
 public:

  template<class T> basic_group(const basic_group<T>& g) {
   std::vector<T> gelems = g.elements();
   int qty = g.order();
   std::map<T,int> elemdex;
   for (int i=0; i<qty; i++) elemdex[gelems[i]] = i;
   table = std::vector< std::vector<int> >(qty, std::vector<int>(qty));
   for (int i=0; i<qty; i++) {
    for (int j=0; j<qty; j++) {
     table[i][j] = elemdex[g.oper(gelems[i], gelems[j])];
    }
   }
   inverses = std::vector<int>(qty);
   orders = std::vector<int>(qty);
   strs = std::vector<std::string>(qty);
   for (int i=0; i<qty; i++) {
    inverses[i] = elemdex[g.invert(gelems[i])];
    orders[i] = g.order(gelems[i]);
    strs[i] = g.showElem(gelems[i]);
   }
   abel = g.abelian();
  }

  basic_group(const basic_group<Element>& t)
   : table(t.table), inverses(t.inverses), orders(t.orders), strs(t.strs),
     abel(t.abel) { }

  virtual ~basic_group() { }

  virtual int oper(const int& x, const int& y) const {return table[x][y]; }

  virtual Element oper(const Element& x, const Element& y) const {
#ifdef GROUP_CHECKS_MEMBERSHIP
   if (!contains(x) || !contains(y)) throw group_mismatch("Group::oper");
#endif
   return Element(this, table[x.val][y.val]);
  }

  virtual Element identity() const {return Element(this, 0); }

  virtual std::vector<Element> elements() const {
   std::vector<Element> elems(table.size(), identity());
   std::vector<Element>::iterator iter = elems.begin();
   int i=0;
   for (iter++, i++; iter != elems.end(); iter++, i++) {
    *iter = Element(this, i);
   }
   return elems;
  }

  virtual int invert(const int& x) const {return inverses[x]; }

  virtual Element invert(const Element& x) const {
#ifdef GROUP_CHECKS_MEMBERSHIP
   if (!contains(x)) throw group_mismatch("Group::invert");
#endif
   return Element(this, inverses[x.val]);
  }

  virtual int order() const {return table.size(); }

  virtual int order(const int& x) const {return orders[x]; }

  virtual int order(const Element& x) const {
#ifdef GROUP_CHECKS_MEMBERSHIP
   if (!contains(x)) throw group_mismatch("Group::order");
#endif
   return orders[x.val];
  }

  virtual std::string showElem(const int& x) const {return strs[x]; }

  virtual std::string showElem(const Element& x) const {
#ifdef GROUP_CHECKS_MEMBERSHIP
   if (!contains(x)) throw group_mismatch("Group::showElem");
#endif
   return strs[x.val];
  }

  virtual bool abelian() const {return abel; }

  virtual basic_group<Element>* copy() const {
   return new basic_group<Element>(*this);
  }

  virtual int cmp(const basic_group<Element>* other) const {
   int ct = cmpTypes(*this, *other);
   if (ct != 0) return ct;
   const basic_group<Element>* c = static_cast<const basic_group<Element>*>(other);
   return table < c->table ? -1 : table > c->table ? 1 : 0;
   /* TODO: Should `strs` also take part in comparisons? */
  }

  virtual bool contains(const int& x) const {return 0 <= x && x < order(); }

  virtual bool contains(const Element& x) const {
   return x.gr->cmp(this) == 0 && 0 <= x.val && x.val < order();
  }

  virtual int indexElem(const Element& x) const {return x.index(); }

  basic_group<Element> direct(const basic_group<Element>& right) {
   int leftQty = order(), rightQty = right.order();
   int newQty = leftQty * rightQty;
   std::vector< std::vector<int> > tbl(newQty, std::vector<int>(newQty));
   for (int i=0; i<newQty; i++) {
    int xa = i / rightQty, xb = i % rightQty;
    for (int j=0; j<newQty; j++) {
     int ya = j / rightQty, yb = j % rightQty;
     tbl[i][j] = table[xa][ya] * rightQty + right.table[xb][yb];
    }
   }
   std::vector<int> invs = std::vector<int>(newQty);
   std::vector<int> ords = std::vector<int>(newQty);
   std::vector<std::string> ss = std::vector<std::string>(newQty);
   for (int i=0; i<newQty; i++) {
    int a = i / rightQty, b = i % rightQty;
    invs[i] = inverses[a] * rightQty + right.inverses[b];
    ords[i] = lcm(orders[a], right.orders[b]);
    if (i == 0) {
     ss[i] = "1";
    } else {
     std::ostringstream out;
     out << '(' << strs[a] << ", " << right.strs[b] << ')';
     ss[i] = out.str();
    }
   }
   return basic_group<Element>(tbl, invs, ords, ss, abel && right.abel);
  }

 private:
  basic_group(std::vector< std::vector<int> > tbl, std::vector<int> invs,
	      std::vector<int> ords, std::vector<std::string> ss, bool abelian)
   : table(tbl), inverses(invs), orders(ords), strs(ss), abel(abelian) { }

  std::vector< std::vector<int> > table;
  std::vector<int> inverses, orders;
  std::vector<std::string> strs;
  bool abel;
 };

 typedef basic_group<Element> Group;
}

#endif
<|endoftext|>