<commit_before>#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <string>
#include <cstring>
#include <iostream>
#include <sstream>
#include <vector>

//Include available regex headers
#ifdef HAVE_PCRE_H
#include <pcre.h>
#endif
#ifdef HAVE_REGEX_H
#include <regex.h>
#endif

#include "regex.hxx"

//Determine support level.
//First check for specific requests from the configuration.
#if defined(FORCE_REGEX_PCRE16)
#  if defined(HAVE_PCRE_H) && defined(PCRE_SUPPORTS_16_BIT)
#    define TGLNG_REGEX_LEVEL TGLNG_REGEX_PCRE16
#  elif !defined(HAVE_PCRE_H)
#    error Configuration forces REGEX_PCRE16, but you do not have PCRE
#  else
#    error Configuration forces REGEX_PCRE16, but 16-bit not supported
#  endif
#elif defined(FORCE_REGEX_PCRE8)
#  if defined(HAVE_PCRE_H)
#    define TGLNG_REGEX_LEVEL TGLNG_REGEX_PCRE8
#  else
#    error Configuration forces REGEX_PCRE8, but you do not have PCRE
#  endif
#elif defined(FORCE_REGEX_POSIX)
#  if defined(HAVE_REGEX_H)
#    define TGLNG_REGEX_LEVEL TGLNG_REGEX_POSIX
#  else
#    error Configuration forces REGEX_POSIX, but your system does not have it
#  endif
#elif defined(FORCE_REGEX_NONE)
#  define TGLNG_REGEX_LEVEL TGLNG_REGEX_NONE
//Not forced, determine automatically
#elif defined(HAVE_PCRE_H) && defined(PCRE_SUPPORTS_16_BIT)
#  define TGLNG_REGEX_LEVEL TGLNG_REGEX_PCRE16
#elif defined(HAVE_PCRE_H)
#  define TGLNG_REGEX_LEVEL TGLNG_REGEX_PCRE8
#elif defined(HAVE_REGEX_H)
#  define TGLNG_REGEX_LEVEL TGLNG_REGEX_POSIX
#else
#  define TGLNG_REGEX_LEVEL TGLNG_REGEX_NONE
#endif

using namespace std;

namespace tglng {
  const unsigned regexLevel = TGLNG_REGEX_LEVEL;
#if TGLNG_REGEX_LEVEL == TGLNG_REGEX_NONE
  const wstring regexLevelName(L"NONE");
#elif TGLNG_REGEX_LEVEL == TGLNG_REGEX_POSIX
  const wstring regexLevelName(L"POSIX");
#elif TGLNG_REGEX_LEVEL == TGLNG_REGEX_PCRE8
  const wstring regexLevelName(L"PCRE8");
#elif TGLNG_REGEX_LEVEL == TGLNG_REGEX_PCRE16
  const wstring regexLevelName(L"PCRE16");
#endif

  //Functions to convert natvie wstrings to the type needed by the backend.
#if TGLNG_REGEX_LEVEL == TGLNG_REGEX_PCRE16
  typedef vector<PCRE_UCHAR16> rstring;
  static void convertString(rstring& dst, const wstring& src) {
    dst.resize(src.size()+1);
    for (unsigned i = 0; i < src.size(); ++i)
      if (src[i] <= 0xFFFF)
        dst[i] = src[i];
      else
        dst[i] = 0x001A;

    dst[src.size()] = 0;
  }
#elif TGLNG_REGEX_LEVEL == TGLNG_REGEX_PCRE8 || \
      TGLNG_REGEX_LEVEL == TGLNG_REGEX_POSIX
  typedef vector<char> rstring;
  static void convertString(rstring& dst, const wstring& src) {
    dst.resize(src.size()+1);
    for (unsigned i = 0; i < src.size(); ++i)
      if (src[i] <= 0xFF)
        dst[i] = (src[i] & 0xFF);
      else
        dst[i] = 0x1A;
    dst[src.size()] = 0;
  }
#endif

#if TGLNG_REGEX_LEVEL == TGLNG_REGEX_NONE
  //Null implementation; always fails at everything
  Regex::Regex(const wstring&, const wstring&)
  : data(*(RegexData*)NULL) {}
  Regex::~Regex() {}
  Regex::operator bool() const { return false; }
  void Regex::showWhy() const {
    wcerr << L"regular expressions not supported in this build." << endl;
  }
  void Regex::input(const wstring&) {}
  bool Regex::match() { return false; }
  unsigned Regex::groupCount() const { return 0; }
  void Regex::group(wstring&, unsigned) const {}
  void Regex::tail(wstring&) const {}
#endif /* NONE */

#if TGLNG_REGEX_LEVEL == TGLNG_REGEX_POSIX
  //POSIX.1-2001 implementation
  struct RegexData {
    regex_t rx;
    int status; //0=OK, others=error
    rstring input;
    wstring rawInput;
    unsigned inputOffset, headBegin, headEnd;
    string why;
    regmatch_t matches[10];
  };

  Regex::Regex(const wstring& pattern, const wstring& options)
  : data(*new RegexData)
  {
    rstring rpattern;
    convertString(rpattern, pattern);

    int flags = REG_EXTENDED;
    //Parse options
    for (unsigned i = 0; i < options.size(); ++i)
      switch (options[i]) {
      case L'i':
        flags |= REG_ICASE;
        break;

      case L'l':
        flags |= REG_NEWLINE;
        break;
      }

    data.status = regcomp(&data.rx, &rpattern[0], flags);
    if (data.status) {
      //Save error message
      data.why.resize(regerror(data.status, &data.rx, NULL, 0));
      regerror(data.status, &data.rx, &data.why[0], data.why.size());
    }
  }

  Regex::~Regex() {
    if (data.status == 0)
      regfree(&data.rx);
    delete &data;
  }

  Regex::operator bool() const {
    return !data.status;
  }

  void Regex::showWhy() const {
    wcerr << "POSIX extended regular expression: "
          << &data.why[0] << endl;
  }

  void Regex::input(const std::wstring& str) {
    convertString(data.input, str);
    data.inputOffset = 0;
    data.rawInput = str;
  }

  bool Regex::match() {
    data.status = regexec(&data.rx, &data.input[data.inputOffset],
                          sizeof(data.matches)/sizeof(data.matches[0]),
                          data.matches, 0);
    if (data.status == REG_NOMATCH) {
      //Transform to a more uniform result
      //(Nothing went wrong, no error should be returned, in theory)
      data.status = 0;
      memset(data.matches, -1, sizeof(data.matches));
    }
    if (data.status) {
      //Failed for some reason
      data.why.resize(regerror(data.status, &data.rx, NULL, 0));
      regerror(data.status, &data.rx, &data.why[0], data.why.size());
      //Free the pattern now, since the destructor won't think it exists
      regfree(&data.rx);
      return false;
    }

    //Matched if the zeroth group is not -1
    if (-1 != data.matches[0].rm_eo) {
      //Update offsets
      data.headBegin = data.inputOffset;
      data.headEnd = data.matches[0].rm_so;
      data.inputOffset = data.matches[0].rm_eo;
    }
    return -1 != data.matches[0].rm_eo;
  }

  unsigned Regex::groupCount() const {
    //Elements in the middle may be unmatched if that particular group was
    //excluded, so search for the last group.
    unsigned last;
    for (unsigned i = 0; i < sizeof(data.matches)/sizeof(data.matches[0]); ++i)
      if (-1 != data.matches[i].rm_so)
        last = i;

    return last+1;
  }

  void Regex::group(wstring& dst, unsigned ix) const {
    //Indices may be negative if this group didn't match
    if (data.matches[ix].rm_so == -1)
      dst.clear();
    else
      dst.assign(data.rawInput, data.matches[ix].rm_so,
                 data.matches[ix].rm_eo - data.matches[ix].rm_so);
  }

  void Regex::tail(wstring& dst) const {
    dst.assign(data.rawInput, data.inputOffset,
               data.rawInput.size() - data.inputOffset);
  }

  void Regex::head(wstring& dst) const {
    dst.assign(data.rawInput, data.headBegin,
               data.headEnd - data.headBegin);
  }
#endif /* POSIX */

#if TGLNG_REGEX_LEVEL == TGLNG_REGEX_PCRE8 ||   \
    TGLNG_REGEX_LEVEL == TGLNG_REGEX_PCRE16
#  if TGLNG_REGEX_LEVEL == TGLNG_REGEX_PCRE8
#    define pcreN pcre
#    define pcreN_compile    pcre_compile
#    define pcreN_exec       pcre_exec
#    define pcreN_free       pcre_free
#    define pcreN_maketables pcre_maketables
#  else
#    define pcreN pcre16
#    define pcreN_compile    pcre16_compile
#    define pcreN_exec       pcre16_exec
#    define pcreN_free       pcre16_free
#    define pcreN_maketables pcre16_maketables
#  endif

  /* By default, PCRE only classifies characters based on ASCII (some builds
   * may instead automatically rebuild the tables according to the "C" locale
   * (why not the system locale?), but we can't count on that.
   *
   * Whenever the current C locale (by setlocale(LC_ALL,NULL)) differs from the
   * lastPcreTableLocale, generate a new table.
   */
  static string lastPcreTableLocale("C");
  static const unsigned char* localPcreTable(NULL);

  struct RegexData {
    pcreN* rx;
    unsigned errorOffset;
    /* We want ten matches. Each match takes two entries. Additionally, PCRE
     * requires that we allocate an extra entry at the end for each match.
     */
#define MAX_MATCHES 10
    signed matches[MAX_MATCHES*3];
    rstring input;
    wstring rawInput;
    unsigned inputOffset, headBegin, headEnd;
    string errorMessage;
  };

  Regex::Regex(const wstring& pattern, const wstring& options)
  : data(*new RegexData)
  {
    rstring rpattern;
    const char* errorMessage = NULL;
    convertString(rpattern, pattern);

    //Parse the options
    int flags = PCRE_DOTALL | PCRE_DOLLAR_ENDONLY;
    for (unsigned i = 0; i < options.size(); ++i)
      switch (options[i]) {
      case L'i':
        flags |= PCRE_CASELESS;
        break;

      case L'l':
        flags &= ~(PCRE_DOTALL | PCRE_DOLLAR_ENDONLY);
        //Would be nice if there were a constant for this
        //(Then again,
        //  flags &= ~(`[PCRE_NEWLINE_`E{CR LF CRLF ANYCRLF ANY}| |`]);
        flags &= ~(PCRE_NEWLINE_CR | PCRE_NEWLINE_LF | PCRE_NEWLINE_CRLF |
                   PCRE_NEWLINE_ANYCRLF | PCRE_NEWLINE_ANY);
        flags |= PCRE_MULTILINE | PCRE_NEWLINE_ANYCRLF;
        break;
      }

    //Generate a table if necessary
    if (lastPcreTableLocale != setlocale(LC_ALL, NULL)) {
      lastPcreTableLocale = setlocale(LC_ALL, NULL);
      if (localPcreTable)
        pcreN_free(const_cast<void*>(static_cast<const void*>(localPcreTable)));
      localPcreTable = pcreN_maketables();
    }

    data.rx = pcreN_compile(&rpattern[0], flags, &errorMessage,
                            (int*)&data.errorOffset, localPcreTable);
    if (!data.rx)
      data.errorMessage = errorMessage;
  }

  Regex::~Regex() {
    if (data.rx)
      pcreN_free(data.rx);
    delete &data;
  }

  Regex::operator bool() const {
    return !!data.rx;
  }

  void Regex::showWhy() const {
    wcerr << L"Perl-Compatible Regular Expression: "
          << data.errorMessage.c_str() << endl;
  }

  void Regex::input(const wstring& str) {
    convertString(data.input, str);
    data.rawInput = str;
    data.inputOffset = 0;
  }

  bool Regex::match() {
    //Set all elements to -1
    memset(data.matches, -1, sizeof(data.matches));
    int status = pcreN_exec(data.rx, NULL,
                            &data.input[0],
                            //Subtract one for term NUL
                            data.input.size() - 1,
                            data.inputOffset,
                            PCRE_NOTEMPTY,
                            data.matches,
                            sizeof(data.matches)/sizeof(data.matches[0]));
    if (status < 0) {
      //Error (there doesn't seem to be any way to get an error message)
      ostringstream msg;
      msg << "Perl-Compatible Regular Expression: error code " << status;
      data.errorMessage = msg.str();
      //Free the rx so that operator bool() returns false
      pcreN_free(data.rx);
      data.rx = NULL;
      return false;
    }

    //PCRE can return 0 even for successful matches (eg, there were more groups
    //than provided), so check for success manually.
    //Any group which was not matched will have entries of -1. If matching is
    //successful, group 0 is defined.
    if (data.matches[0] != -1) {
      //Advance input
      data.headBegin = data.inputOffset;
      data.headEnd = data.matches[0];
      data.inputOffset = data.matches[1];
      return true;
    } else {
      return false;
    }
  }

  unsigned Regex::groupCount() const {
    //PCRE groups are indexed by occurrance within the pattern, so some groups
    //might not match; find the last matched group.
    unsigned last = 0;
    for (unsigned i = 0; i < MAX_MATCHES; ++i)
      if (data.matches[i*2] != -1)
        last = i;

    return last+1;
  }

  void Regex::group(wstring& dst, unsigned ix) const {
    //Some middle groups may be unmatched, so return an empty string if -1
    if (data.matches[ix*2] == -1)
      dst.clear();
    else
      dst.assign(data.rawInput, data.matches[ix*2],
                 data.matches[ix*2+1] - data.matches[ix*2]);
  }

  void Regex::head(wstring& dst) const {
    dst.assign(data.rawInput, data.headBegin,
               data.headEnd - data.headBegin);
  }

  void Regex::tail(wstring& dst) const {
    dst.assign(data.rawInput, data.inputOffset,
               data.rawInput.size() - data.inputOffset);
  }
#endif /* PCRE* */
}
<commit_msg>Fix missing Regex::head() in no-op regex engine wrapper.<commit_after>#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <string>
#include <cstring>
#include <iostream>
#include <sstream>
#include <vector>

//Include available regex headers
#ifdef HAVE_PCRE_H
#include <pcre.h>
#endif
#ifdef HAVE_REGEX_H
#include <regex.h>
#endif

#include "regex.hxx"

//Determine support level.
//First check for specific requests from the configuration.
#if defined(FORCE_REGEX_PCRE16)
#  if defined(HAVE_PCRE_H) && defined(PCRE_SUPPORTS_16_BIT)
#    define TGLNG_REGEX_LEVEL TGLNG_REGEX_PCRE16
#  elif !defined(HAVE_PCRE_H)
#    error Configuration forces REGEX_PCRE16, but you do not have PCRE
#  else
#    error Configuration forces REGEX_PCRE16, but 16-bit not supported
#  endif
#elif defined(FORCE_REGEX_PCRE8)
#  if defined(HAVE_PCRE_H)
#    define TGLNG_REGEX_LEVEL TGLNG_REGEX_PCRE8
#  else
#    error Configuration forces REGEX_PCRE8, but you do not have PCRE
#  endif
#elif defined(FORCE_REGEX_POSIX)
#  if defined(HAVE_REGEX_H)
#    define TGLNG_REGEX_LEVEL TGLNG_REGEX_POSIX
#  else
#    error Configuration forces REGEX_POSIX, but your system does not have it
#  endif
#elif defined(FORCE_REGEX_NONE)
#  define TGLNG_REGEX_LEVEL TGLNG_REGEX_NONE
//Not forced, determine automatically
#elif defined(HAVE_PCRE_H) && defined(PCRE_SUPPORTS_16_BIT)
#  define TGLNG_REGEX_LEVEL TGLNG_REGEX_PCRE16
#elif defined(HAVE_PCRE_H)
#  define TGLNG_REGEX_LEVEL TGLNG_REGEX_PCRE8
#elif defined(HAVE_REGEX_H)
#  define TGLNG_REGEX_LEVEL TGLNG_REGEX_POSIX
#else
#  define TGLNG_REGEX_LEVEL TGLNG_REGEX_NONE
#endif

using namespace std;

namespace tglng {
  const unsigned regexLevel = TGLNG_REGEX_LEVEL;
#if TGLNG_REGEX_LEVEL == TGLNG_REGEX_NONE
  const wstring regexLevelName(L"NONE");
#elif TGLNG_REGEX_LEVEL == TGLNG_REGEX_POSIX
  const wstring regexLevelName(L"POSIX");
#elif TGLNG_REGEX_LEVEL == TGLNG_REGEX_PCRE8
  const wstring regexLevelName(L"PCRE8");
#elif TGLNG_REGEX_LEVEL == TGLNG_REGEX_PCRE16
  const wstring regexLevelName(L"PCRE16");
#endif

  //Functions to convert natvie wstrings to the type needed by the backend.
#if TGLNG_REGEX_LEVEL == TGLNG_REGEX_PCRE16
  typedef vector<PCRE_UCHAR16> rstring;
  static void convertString(rstring& dst, const wstring& src) {
    dst.resize(src.size()+1);
    for (unsigned i = 0; i < src.size(); ++i)
      if (src[i] <= 0xFFFF)
        dst[i] = src[i];
      else
        dst[i] = 0x001A;

    dst[src.size()] = 0;
  }
#elif TGLNG_REGEX_LEVEL == TGLNG_REGEX_PCRE8 || \
      TGLNG_REGEX_LEVEL == TGLNG_REGEX_POSIX
  typedef vector<char> rstring;
  static void convertString(rstring& dst, const wstring& src) {
    dst.resize(src.size()+1);
    for (unsigned i = 0; i < src.size(); ++i)
      if (src[i] <= 0xFF)
        dst[i] = (src[i] & 0xFF);
      else
        dst[i] = 0x1A;
    dst[src.size()] = 0;
  }
#endif

#if TGLNG_REGEX_LEVEL == TGLNG_REGEX_NONE
  //Null implementation; always fails at everything
  Regex::Regex(const wstring&, const wstring&)
  : data(*(RegexData*)NULL) {}
  Regex::~Regex() {}
  Regex::operator bool() const { return false; }
  void Regex::showWhy() const {
    wcerr << L"regular expressions not supported in this build." << endl;
  }
  void Regex::input(const wstring&) {}
  bool Regex::match() { return false; }
  unsigned Regex::groupCount() const { return 0; }
  void Regex::group(wstring&, unsigned) const {}
  void Regex::tail(wstring&) const {}
  void Regex::head(wstring&) const {}
#endif /* NONE */

#if TGLNG_REGEX_LEVEL == TGLNG_REGEX_POSIX
  //POSIX.1-2001 implementation
  struct RegexData {
    regex_t rx;
    int status; //0=OK, others=error
    rstring input;
    wstring rawInput;
    unsigned inputOffset, headBegin, headEnd;
    string why;
    regmatch_t matches[10];
  };

  Regex::Regex(const wstring& pattern, const wstring& options)
  : data(*new RegexData)
  {
    rstring rpattern;
    convertString(rpattern, pattern);

    int flags = REG_EXTENDED;
    //Parse options
    for (unsigned i = 0; i < options.size(); ++i)
      switch (options[i]) {
      case L'i':
        flags |= REG_ICASE;
        break;

      case L'l':
        flags |= REG_NEWLINE;
        break;
      }

    data.status = regcomp(&data.rx, &rpattern[0], flags);
    if (data.status) {
      //Save error message
      data.why.resize(regerror(data.status, &data.rx, NULL, 0));
      regerror(data.status, &data.rx, &data.why[0], data.why.size());
    }
  }

  Regex::~Regex() {
    if (data.status == 0)
      regfree(&data.rx);
    delete &data;
  }

  Regex::operator bool() const {
    return !data.status;
  }

  void Regex::showWhy() const {
    wcerr << "POSIX extended regular expression: "
          << &data.why[0] << endl;
  }

  void Regex::input(const std::wstring& str) {
    convertString(data.input, str);
    data.inputOffset = 0;
    data.rawInput = str;
  }

  bool Regex::match() {
    data.status = regexec(&data.rx, &data.input[data.inputOffset],
                          sizeof(data.matches)/sizeof(data.matches[0]),
                          data.matches, 0);
    if (data.status == REG_NOMATCH) {
      //Transform to a more uniform result
      //(Nothing went wrong, no error should be returned, in theory)
      data.status = 0;
      memset(data.matches, -1, sizeof(data.matches));
    }
    if (data.status) {
      //Failed for some reason
      data.why.resize(regerror(data.status, &data.rx, NULL, 0));
      regerror(data.status, &data.rx, &data.why[0], data.why.size());
      //Free the pattern now, since the destructor won't think it exists
      regfree(&data.rx);
      return false;
    }

    //Matched if the zeroth group is not -1
    if (-1 != data.matches[0].rm_eo) {
      //Update offsets
      data.headBegin = data.inputOffset;
      data.headEnd = data.matches[0].rm_so;
      data.inputOffset = data.matches[0].rm_eo;
    }
    return -1 != data.matches[0].rm_eo;
  }

  unsigned Regex::groupCount() const {
    //Elements in the middle may be unmatched if that particular group was
    //excluded, so search for the last group.
    unsigned last;
    for (unsigned i = 0; i < sizeof(data.matches)/sizeof(data.matches[0]); ++i)
      if (-1 != data.matches[i].rm_so)
        last = i;

    return last+1;
  }

  void Regex::group(wstring& dst, unsigned ix) const {
    //Indices may be negative if this group didn't match
    if (data.matches[ix].rm_so == -1)
      dst.clear();
    else
      dst.assign(data.rawInput, data.matches[ix].rm_so,
                 data.matches[ix].rm_eo - data.matches[ix].rm_so);
  }

  void Regex::tail(wstring& dst) const {
    dst.assign(data.rawInput, data.inputOffset,
               data.rawInput.size() - data.inputOffset);
  }

  void Regex::head(wstring& dst) const {
    dst.assign(data.rawInput, data.headBegin,
               data.headEnd - data.headBegin);
  }
#endif /* POSIX */

#if TGLNG_REGEX_LEVEL == TGLNG_REGEX_PCRE8 ||   \
    TGLNG_REGEX_LEVEL == TGLNG_REGEX_PCRE16
#  if TGLNG_REGEX_LEVEL == TGLNG_REGEX_PCRE8
#    define pcreN pcre
#    define pcreN_compile    pcre_compile
#    define pcreN_exec       pcre_exec
#    define pcreN_free       pcre_free
#    define pcreN_maketables pcre_maketables
#  else
#    define pcreN pcre16
#    define pcreN_compile    pcre16_compile
#    define pcreN_exec       pcre16_exec
#    define pcreN_free       pcre16_free
#    define pcreN_maketables pcre16_maketables
#  endif

  /* By default, PCRE only classifies characters based on ASCII (some builds
   * may instead automatically rebuild the tables according to the "C" locale
   * (why not the system locale?), but we can't count on that.
   *
   * Whenever the current C locale (by setlocale(LC_ALL,NULL)) differs from the
   * lastPcreTableLocale, generate a new table.
   */
  static string lastPcreTableLocale("C");
  static const unsigned char* localPcreTable(NULL);

  struct RegexData {
    pcreN* rx;
    unsigned errorOffset;
    /* We want ten matches. Each match takes two entries. Additionally, PCRE
     * requires that we allocate an extra entry at the end for each match.
     */
#define MAX_MATCHES 10
    signed matches[MAX_MATCHES*3];
    rstring input;
    wstring rawInput;
    unsigned inputOffset, headBegin, headEnd;
    string errorMessage;
  };

  Regex::Regex(const wstring& pattern, const wstring& options)
  : data(*new RegexData)
  {
    rstring rpattern;
    const char* errorMessage = NULL;
    convertString(rpattern, pattern);

    //Parse the options
    int flags = PCRE_DOTALL | PCRE_DOLLAR_ENDONLY;
    for (unsigned i = 0; i < options.size(); ++i)
      switch (options[i]) {
      case L'i':
        flags |= PCRE_CASELESS;
        break;

      case L'l':
        flags &= ~(PCRE_DOTALL | PCRE_DOLLAR_ENDONLY);
        //Would be nice if there were a constant for this
        //(Then again,
        //  flags &= ~(`[PCRE_NEWLINE_`E{CR LF CRLF ANYCRLF ANY}| |`]);
        flags &= ~(PCRE_NEWLINE_CR | PCRE_NEWLINE_LF | PCRE_NEWLINE_CRLF |
                   PCRE_NEWLINE_ANYCRLF | PCRE_NEWLINE_ANY);
        flags |= PCRE_MULTILINE | PCRE_NEWLINE_ANYCRLF;
        break;
      }

    //Generate a table if necessary
    if (lastPcreTableLocale != setlocale(LC_ALL, NULL)) {
      lastPcreTableLocale = setlocale(LC_ALL, NULL);
      if (localPcreTable)
        pcreN_free(const_cast<void*>(static_cast<const void*>(localPcreTable)));
      localPcreTable = pcreN_maketables();
    }

    data.rx = pcreN_compile(&rpattern[0], flags, &errorMessage,
                            (int*)&data.errorOffset, localPcreTable);
    if (!data.rx)
      data.errorMessage = errorMessage;
  }

  Regex::~Regex() {
    if (data.rx)
      pcreN_free(data.rx);
    delete &data;
  }

  Regex::operator bool() const {
    return !!data.rx;
  }

  void Regex::showWhy() const {
    wcerr << L"Perl-Compatible Regular Expression: "
          << data.errorMessage.c_str() << endl;
  }

  void Regex::input(const wstring& str) {
    convertString(data.input, str);
    data.rawInput = str;
    data.inputOffset = 0;
  }

  bool Regex::match() {
    //Set all elements to -1
    memset(data.matches, -1, sizeof(data.matches));
    int status = pcreN_exec(data.rx, NULL,
                            &data.input[0],
                            //Subtract one for term NUL
                            data.input.size() - 1,
                            data.inputOffset,
                            PCRE_NOTEMPTY,
                            data.matches,
                            sizeof(data.matches)/sizeof(data.matches[0]));
    if (status < 0) {
      //Error (there doesn't seem to be any way to get an error message)
      ostringstream msg;
      msg << "Perl-Compatible Regular Expression: error code " << status;
      data.errorMessage = msg.str();
      //Free the rx so that operator bool() returns false
      pcreN_free(data.rx);
      data.rx = NULL;
      return false;
    }

    //PCRE can return 0 even for successful matches (eg, there were more groups
    //than provided), so check for success manually.
    //Any group which was not matched will have entries of -1. If matching is
    //successful, group 0 is defined.
    if (data.matches[0] != -1) {
      //Advance input
      data.headBegin = data.inputOffset;
      data.headEnd = data.matches[0];
      data.inputOffset = data.matches[1];
      return true;
    } else {
      return false;
    }
  }

  unsigned Regex::groupCount() const {
    //PCRE groups are indexed by occurrance within the pattern, so some groups
    //might not match; find the last matched group.
    unsigned last = 0;
    for (unsigned i = 0; i < MAX_MATCHES; ++i)
      if (data.matches[i*2] != -1)
        last = i;

    return last+1;
  }

  void Regex::group(wstring& dst, unsigned ix) const {
    //Some middle groups may be unmatched, so return an empty string if -1
    if (data.matches[ix*2] == -1)
      dst.clear();
    else
      dst.assign(data.rawInput, data.matches[ix*2],
                 data.matches[ix*2+1] - data.matches[ix*2]);
  }

  void Regex::head(wstring& dst) const {
    dst.assign(data.rawInput, data.headBegin,
               data.headEnd - data.headBegin);
  }

  void Regex::tail(wstring& dst) const {
    dst.assign(data.rawInput, data.inputOffset,
               data.rawInput.size() - data.inputOffset);
  }
#endif /* PCRE* */
}
<|endoftext|>


<commit_before>/******************************************************************************

  This source file is part of the tomviz project.

  Copyright Kitware, Inc.

  This source code is released under the New BSD License, (the "License").

  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 "SaveDataReaction.h"

#include "ActiveObjects.h"
#include "DataSource.h"
#include "ModuleManager.h"
#include "pqActiveObjects.h"
#include "pqCoreUtilities.h"
#include "pqSaveDataReaction.h"
#include "pqPipelineSource.h"
#include "pqProxyWidgetDialog.h"
#include "pqFileDialog.h"
#include "pqWriterDialog.h"
#include "vtkSMCoreUtilities.h"
#include "vtkSMParaViewPipelineController.h"
#include "vtkSMPropertyHelper.h"
#include "vtkSMProxyManager.h"
#include "vtkSMSessionProxyManager.h"
#include "vtkSMSourceProxy.h"
#include "vtkSMWriterFactory.h"

#include <QDebug>

namespace tomviz
{
//-----------------------------------------------------------------------------
SaveDataReaction::SaveDataReaction(QAction* parentObject)
  : Superclass(parentObject)
{
}

//-----------------------------------------------------------------------------
SaveDataReaction::~SaveDataReaction()
{
}

//-----------------------------------------------------------------------------
void SaveDataReaction::onTriggered()
{
  pqServer* server = pqActiveObjects::instance().activeServer();
  DataSource *source = ActiveObjects::instance().activeDataSource();
  vtkSMWriterFactory* writerFactory =
      vtkSMProxyManager::GetProxyManager()->GetWriterFactory();
  QString filters = writerFactory->GetSupportedFileTypes(source->producer());
  if (filters.isEmpty())
    {
    qCritical("Cannot determine writer to use.");
    return;
    }

  pqFileDialog fileDialog(server,
                          pqCoreUtilities::mainWidget(),
                          tr("Save File:"), QString(), filters);
  // FIXME: fileDialog.setRecentlyUsedExtension(this->DataExtension);
  fileDialog.setObjectName("FileSaveDialog");
  fileDialog.setFileMode(pqFileDialog::AnyFile);
  if (fileDialog.exec() == QDialog::Accepted)
    {
    this->saveData(fileDialog.getSelectedFiles()[0]);
    }
}

bool SaveDataReaction::saveData(const QString &filename)
{
  pqServer* server = pqActiveObjects::instance().activeServer();
  DataSource *source = ActiveObjects::instance().activeDataSource();
  if (!server || !source)
    {
    qCritical("No active source located.");
    return false;
    }

  vtkSMWriterFactory* writerFactory = vtkSMProxyManager::GetProxyManager()->GetWriterFactory();
  vtkSmartPointer<vtkSMProxy> proxy;
  proxy.TakeReference(writerFactory->CreateWriter(filename.toLatin1().data(),
                      source->producer()));
  vtkSMSourceProxy* writer = vtkSMSourceProxy::SafeDownCast(proxy);
  if (!writer)
    {
    qCritical() << "Failed to create writer for: " << filename;
    return false;
    }

  pqWriterDialog dialog(writer);

  // Check to see if this writer has any properties that can be configured by
  // the user. If it does, display the dialog.
  if (dialog.hasConfigurableProperties())
    {
    dialog.exec();
    if(dialog.result() == QDialog::Rejected)
      {
      // The user pressed Cancel so don't write
      return false;
      }
    }
  writer->UpdateVTKObjects();
  writer->UpdatePipeline();
  return true;
}

} // end of namespace tomviz
<commit_msg>Default to saving data as tiff files -- fixes #155<commit_after>/******************************************************************************

  This source file is part of the tomviz project.

  Copyright Kitware, Inc.

  This source code is released under the New BSD License, (the "License").

  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 "SaveDataReaction.h"

#include "ActiveObjects.h"
#include "DataSource.h"
#include "ModuleManager.h"
#include "pqActiveObjects.h"
#include "pqCoreUtilities.h"
#include "pqSaveDataReaction.h"
#include "pqPipelineSource.h"
#include "pqProxyWidgetDialog.h"
#include "pqFileDialog.h"
#include "pqWriterDialog.h"
#include "vtkSMCoreUtilities.h"
#include "vtkSMParaViewPipelineController.h"
#include "vtkSMPropertyHelper.h"
#include "vtkSMProxyManager.h"
#include "vtkSMSessionProxyManager.h"
#include "vtkSMSourceProxy.h"
#include "vtkSMWriterFactory.h"

#include <QDebug>

namespace tomviz
{
//-----------------------------------------------------------------------------
SaveDataReaction::SaveDataReaction(QAction* parentObject)
  : Superclass(parentObject)
{
}

//-----------------------------------------------------------------------------
SaveDataReaction::~SaveDataReaction()
{
}

//-----------------------------------------------------------------------------
void SaveDataReaction::onTriggered()
{
  pqServer* server = pqActiveObjects::instance().activeServer();
  DataSource *source = ActiveObjects::instance().activeDataSource();
  vtkSMWriterFactory* writerFactory =
      vtkSMProxyManager::GetProxyManager()->GetWriterFactory();
  QString filters = writerFactory->GetSupportedFileTypes(source->producer());
  if (filters.isEmpty())
    {
    qCritical("Cannot determine writer to use.");
    return;
    }

  pqFileDialog fileDialog(server,
                          pqCoreUtilities::mainWidget(),
                          tr("Save File:"), QString(), filters);
  fileDialog.setObjectName("FileSaveDialog");
  fileDialog.setFileMode(pqFileDialog::AnyFile);
  // Default to saving tiff files
  fileDialog.setRecentlyUsedExtension(".tiff");
  if (fileDialog.exec() == QDialog::Accepted)
    {
    this->saveData(fileDialog.getSelectedFiles()[0]);
    }
}

bool SaveDataReaction::saveData(const QString &filename)
{
  pqServer* server = pqActiveObjects::instance().activeServer();
  DataSource *source = ActiveObjects::instance().activeDataSource();
  if (!server || !source)
    {
    qCritical("No active source located.");
    return false;
    }

  vtkSMWriterFactory* writerFactory = vtkSMProxyManager::GetProxyManager()->GetWriterFactory();
  vtkSmartPointer<vtkSMProxy> proxy;
  proxy.TakeReference(writerFactory->CreateWriter(filename.toLatin1().data(),
                      source->producer()));
  vtkSMSourceProxy* writer = vtkSMSourceProxy::SafeDownCast(proxy);
  if (!writer)
    {
    qCritical() << "Failed to create writer for: " << filename;
    return false;
    }

  pqWriterDialog dialog(writer);

  // Check to see if this writer has any properties that can be configured by
  // the user. If it does, display the dialog.
  if (dialog.hasConfigurableProperties())
    {
    dialog.exec();
    if(dialog.result() == QDialog::Rejected)
      {
      // The user pressed Cancel so don't write
      return false;
      }
    }
  writer->UpdateVTKObjects();
  writer->UpdatePipeline();
  return true;
}

} // end of namespace tomviz
<|endoftext|>


<commit_before><commit_msg>remove unused struct<commit_after><|endoftext|>


<commit_before>#include "DialogoEditarConcepto.h"
#include "ui_DialogoEditarConcepto.h"

// utiles
#include <utiles/include/FuncionesString.h>

DialogoEditarConcepto::DialogoEditarConcepto(visualizador::modelo::Concepto * concepto_a_editar, visualizador::aplicacion::GestorEntidades * gestor_terminos, QWidget *parent)
    : concepto_a_editar(concepto_a_editar), gestor_terminos(gestor_terminos), QDialog(parent)
{
    ui = new Ui::DialogoEditarConcepto();
    ui->setupUi(this);

    this->conectar_componentes();

    aplicacion::Logger::info("Iniciando dialogo Editar Conceptos.");

    this->setAttribute(Qt::WA_DeleteOnClose);

    this->ui->lineedit_etiqueta->setText(concepto_a_editar->getEtiqueta().c_str());
    this->etiqueta_original = concepto_a_editar->getEtiqueta();

    this->cargarListaTerminos(concepto_a_editar);
}

DialogoEditarConcepto::~DialogoEditarConcepto()
{
    this->descargarListaTerminos();

    aplicacion::Logger::info("Cerrando dialogo Conceptos.");

    delete ui;
}

void DialogoEditarConcepto::eliminar()
{
    QList<QListWidgetItem*> items = ui->lista->selectedItems();
    foreach(QListWidgetItem * item, items)
    {
        QVariant data = item->data(Qt::UserRole);
        modelo::Termino* termino = data.value<modelo::Termino*>();

        this->gestor_terminos_de_concepto.eliminar(termino);

        aplicacion::Logger::info("Termino eliminado: { " + aplicacion::Logger::infoLog(termino) + " }.");

        if (0 == termino->restarReferencia())
        {
            delete termino;
        }

        delete this->ui->lista->takeItem(ui->lista->row(item));
    }
}

void DialogoEditarConcepto::nuevo()
{
    std::string texto_nuevo_valor = "<nuevo valor>";
    this->termino_sin_editar = texto_nuevo_valor;

    visualizador::modelo::Termino * nuevo_termino = new visualizador::modelo::Termino(texto_nuevo_valor);
    nuevo_termino->sumarReferencia();

    QListWidgetItem* item = new QListWidgetItem();

    item->setText(texto_nuevo_valor.c_str());
    item->setFlags(item->flags() | Qt::ItemIsEditable);
    QVariant data = QVariant::fromValue(nuevo_termino);

    this->ui->lista->blockSignals(true);
    item->setData(Qt::UserRole, data);
    this->ui->lista->blockSignals(false);

    this->ui->lista->insertItem(0, item);

    this->ui->lista->editItem(item);
}

void DialogoEditarConcepto::guardar()
{
    if (false == this->etiquetaModificada() && false == this->listaDeTerminosModificada())
    {
        this->close();
        return;
    }

    if (this->ui->lineedit_etiqueta->text().isEmpty())
    {
        QMessageBox * informacion_etiqueta_vacia = this->crearInformacionEtiquetaVacia();
        informacion_etiqueta_vacia->exec();

        delete informacion_etiqueta_vacia;

        return;
    }

    if (0 == this->ui->lista->count())
    {
        QMessageBox * informacion_lista_de_terminos_vacia = this->crearInformacionListaDeTerminosVacia();
        informacion_lista_de_terminos_vacia->exec();

        delete informacion_lista_de_terminos_vacia;

        return;
    }

    this->concepto_a_editar->setEtiqueta(this->ui->lineedit_etiqueta->text().toStdString());

    std::vector<visualizador::modelo::IEntidad*> entidades_a_almacenar = this->gestor_terminos_de_concepto.getEntidadesAAlmacenar();
    for (std::vector<visualizador::modelo::IEntidad*>::iterator it = entidades_a_almacenar.begin(); it != entidades_a_almacenar.end(); it++)
    {
        this->gestor_terminos->almacenar(*it);

        this->concepto_a_editar->agregarTermino(this->gestor_terminos->clonar<visualizador::modelo::Termino>(*it));
    }

    std::vector<visualizador::modelo::IEntidad*> entidades_a_eliminar = this->gestor_terminos_de_concepto.getEntidadesAEliminar();
    for (std::vector<visualizador::modelo::IEntidad*>::iterator it = entidades_a_eliminar.begin(); it != entidades_a_eliminar.end(); it++)
    {
        visualizador::modelo::Termino * termino_a_sacar = this->gestor_terminos->clonar<visualizador::modelo::Termino>(*it);
        this->concepto_a_editar->sacarTermino(termino_a_sacar);
        delete termino_a_sacar;
    }

    this->accept();
}

void DialogoEditarConcepto::termino_actualizado(QListWidgetItem * item_actualizado)
{
    std::string termino_normalizado = item_actualizado->text().toStdString();

    if (false == this->normalizar(&termino_normalizado)) {
        QMessageBox * informacion_termino_invalido = this->crearInformacionTerminoInvalido();
        informacion_termino_invalido->exec();
        this->ui->lista->blockSignals(true);
        item_actualizado->setText(QString(this->termino_sin_editar.c_str()));
        this->ui->lista->blockSignals(false);
        return;
    }

    visualizador::modelo::Termino * nuevo_termino = new visualizador::modelo::Termino(termino_normalizado);

    if (this->termino_sin_editar == nuevo_termino->getValor())
    {// si se deja el mismo valor, entonces no se hace nada.
        delete nuevo_termino;

        return;
    }

    if (this->gestor_terminos_de_concepto.existe(nuevo_termino))
    {// si ya hay un termino con el mismo valor, entonces se vuelve al valor anterior.
        QMessageBox * informacion_termino_existente = this->crearInformacionTerminoExistente();
        informacion_termino_existente->exec();

        this->ui->lista->blockSignals(true);
        item_actualizado->setText(QString(this->termino_sin_editar.c_str()));
        this->ui->lista->blockSignals(false);

        delete informacion_termino_existente;

        delete nuevo_termino;

        return;
    }
    item_actualizado->setText(QString(termino_normalizado.c_str()));

    visualizador::modelo::Termino * termino_a_agregar_a_lista = NULL;

    visualizador::modelo::Termino * termino_encontrado = this->gestor_terminos->encontrar(nuevo_termino);
    if (NULL != termino_encontrado)
    {// si lo encontro, entonces el termino ya existe. lo que hago es clonarlo y usar el clon, y dsp reemplazarlo en la lista visible.
        termino_a_agregar_a_lista = this->gestor_terminos->clonar<visualizador::modelo::Termino>(termino_encontrado);
        delete nuevo_termino;
    }
    else
    {// si no lo encontro, entonces el termino es nuevo. lo agrego a la lista de terminos a agregar.
        termino_a_agregar_a_lista = nuevo_termino;
    }
    this->gestor_terminos_de_concepto.almacenar(termino_a_agregar_a_lista);
    termino_a_agregar_a_lista->sumarReferencia();

    visualizador::modelo::Termino * termino_lista = item_actualizado->data(Qt::UserRole).value<visualizador::modelo::Termino*>();

    if (NULL != termino_lista && 0 == termino_lista->restarReferencia())
    {// si el item ya tiene asignado un termino, entonces lo elimino.
        delete termino_lista;
    }

    QVariant data = QVariant::fromValue(termino_a_agregar_a_lista);
    this->ui->lista->blockSignals(true);
    item_actualizado->setData(Qt::UserRole, data);
    this->ui->lista->blockSignals(false);
}

void DialogoEditarConcepto::guardar_termino_sin_editar(QListWidgetItem * item_actual, QListWidgetItem * item_previo)
{
    if(NULL != item_actual)
    {
        this->termino_sin_editar = item_actual->text().toStdString();
    }
}

// METODOS INTERNOS

bool DialogoEditarConcepto::normalizar(std::string * termino) {

    std::string termino_original = *termino;

    herramientas::utiles::FuncionesString::eliminar_tildes_utf8(termino);
    herramientas::utiles::FuncionesString::todoMinuscula(*termino);
    herramientas::utiles::FuncionesString::eliminarSignosYPuntuacion(*termino, { "*" });
    herramientas::utiles::FuncionesString::eliminarCaracteresDeControl(*termino);
    herramientas::utiles::FuncionesString::eliminarEspaciosRedundantes(*termino);
    herramientas::utiles::FuncionesString::recortar(termino);

    if (herramientas::utiles::FuncionesString::separar(*termino).size() > 3) {
        *termino = termino_original;
        return false;
    }

    return true;
}

bool DialogoEditarConcepto::etiquetaModificada()
{
    return this->ui->lineedit_etiqueta->text().toStdString() != this->etiqueta_original;
}

bool DialogoEditarConcepto::listaDeTerminosModificada()
{
    return (this->gestor_terminos_de_concepto.getEntidadesAAlmacenar().size() != 0 || this->gestor_terminos_de_concepto.getEntidadesAEliminar().size() != 0);
}

void DialogoEditarConcepto::cargarListaTerminos(visualizador::modelo::Concepto * concepto_a_editar)
{
    std::vector<visualizador::modelo::Termino*> terminos_actuales = concepto_a_editar->getTerminos();

    for (std::vector<visualizador::modelo::Termino*>::iterator it = terminos_actuales.begin(); it != terminos_actuales.end(); it++)
    {
        //(*it)->sumarReferencia();
        std::string texto_termino = (*it)->getValor();
        QListWidgetItem* item = new QListWidgetItem();

        visualizador::modelo::Termino* termino_item = this->gestor_terminos->clonar<visualizador::modelo::Termino>(*it);
        termino_item->sumarReferencia();
        QVariant data = QVariant::fromValue(termino_item);
        item->setData(Qt::UserRole, data);
        item->setText(texto_termino.c_str());
        item->setFlags(item->flags() | Qt::ItemIsEditable);

        this->ui->lista->insertItem(0, item);
    }

    this->gestor_terminos_de_concepto.gestionar<visualizador::modelo::Termino>(terminos_actuales);

    aplicacion::Logger::info(std::to_string(terminos_actuales.size()) + " terminos cargados.");

    this->ui->lista->setSelectionMode(QAbstractItemView::SelectionMode::ExtendedSelection);
}

void DialogoEditarConcepto::descargarListaTerminos()
{
    QListWidgetItem * item = nullptr;

    // elimino los terminos de la lista
    visualizador::modelo::Termino * termino_lista = nullptr;
    unsigned int count = ui->lista->count();
    while (0 != ui->lista->count())
    {
        count = ui->lista->count();

        termino_lista = this->ui->lista->item(0)->data(Qt::UserRole).value<visualizador::modelo::Termino*>();

        if (0 == termino_lista->restarReferencia())
        {
            delete termino_lista;
        }

        item = this->ui->lista->takeItem(0);
        delete item;
    }

    aplicacion::Logger::info(std::to_string(count) + " terminos descargados.");
}

// mensajes

QMessageBox * DialogoEditarConcepto::crearInformacionTerminoExistente()
{
    std::string texto = u8"El trmino que se quiere agregar ya existe.";
    visualizador::aplicacion::comunicacion::Informacion informacion_termino_existente(texto);
    return comunicacion::FabricaMensajes::fabricar(&informacion_termino_existente, this);
}

QMessageBox * DialogoEditarConcepto::crearInformacionTerminoInvalido() {
    std::string texto = u8"Imposible crear trmino con ms de una palabra. El trmino debe ser exactamente de una sola palabra.";
    visualizador::aplicacion::comunicacion::Informacion informacion_termino_invalido(texto);
    return comunicacion::FabricaMensajes::fabricar(&informacion_termino_invalido, this);
}

QMessageBox * DialogoEditarConcepto::crearInformacionEtiquetaVacia()
{
    std::string texto = u8"No se asigno la etiqueta. La etiqueta sirve para describir e identificar al concepto en las consultas.";
    visualizador::aplicacion::comunicacion::Informacion informacion_etiqueta_vacia(texto);
    return comunicacion::FabricaMensajes::fabricar(&informacion_etiqueta_vacia, this);
}

QMessageBox * DialogoEditarConcepto::crearInformacionListaDeTerminosVacia()
{
    std::string texto = u8"El concepto no tiene trminos. La lista de trminos no puede estar vaca.";
    visualizador::aplicacion::comunicacion::Informacion informacion_etiqueta_vacia(texto);
    return comunicacion::FabricaMensajes::fabricar(&informacion_etiqueta_vacia, this);
}

void DialogoEditarConcepto::conectar_componentes() {

    QObject::connect(this->ui->btn_nuevo, &QPushButton::released, this, &DialogoEditarConcepto::nuevo);
    QObject::connect(this->ui->btn_eliminar, &QPushButton::released, this, &DialogoEditarConcepto::eliminar);
    QObject::connect(this->ui->btn_guardar, &QPushButton::released, this, &DialogoEditarConcepto::guardar);
    QObject::connect(this->ui->btn_cancelar, &QPushButton::released, this, &DialogoEditarConcepto::close);

    QObject::connect(this->ui->lista, &QListWidget::itemChanged, this, &DialogoEditarConcepto::termino_actualizado);
    QObject::connect(this->ui->lista, &QListWidget::currentItemChanged, this, &DialogoEditarConcepto::guardar_termino_sin_editar);
}<commit_msg>termino acepta hasta 3 palabras<commit_after>#include "DialogoEditarConcepto.h"
#include "ui_DialogoEditarConcepto.h"

// utiles
#include <utiles/include/FuncionesString.h>

DialogoEditarConcepto::DialogoEditarConcepto(visualizador::modelo::Concepto * concepto_a_editar, visualizador::aplicacion::GestorEntidades * gestor_terminos, QWidget *parent)
    : concepto_a_editar(concepto_a_editar), gestor_terminos(gestor_terminos), QDialog(parent)
{
    ui = new Ui::DialogoEditarConcepto();
    ui->setupUi(this);

    this->conectar_componentes();

    aplicacion::Logger::info("Iniciando dialogo Editar Conceptos.");

    this->setAttribute(Qt::WA_DeleteOnClose);

    this->ui->lineedit_etiqueta->setText(concepto_a_editar->getEtiqueta().c_str());
    this->etiqueta_original = concepto_a_editar->getEtiqueta();

    this->cargarListaTerminos(concepto_a_editar);
}

DialogoEditarConcepto::~DialogoEditarConcepto()
{
    this->descargarListaTerminos();

    aplicacion::Logger::info("Cerrando dialogo Conceptos.");

    delete ui;
}

void DialogoEditarConcepto::eliminar()
{
    QList<QListWidgetItem*> items = ui->lista->selectedItems();
    foreach(QListWidgetItem * item, items)
    {
        QVariant data = item->data(Qt::UserRole);
        modelo::Termino* termino = data.value<modelo::Termino*>();

        this->gestor_terminos_de_concepto.eliminar(termino);

        aplicacion::Logger::info("Termino eliminado: { " + aplicacion::Logger::infoLog(termino) + " }.");

        if (0 == termino->restarReferencia())
        {
            delete termino;
        }

        delete this->ui->lista->takeItem(ui->lista->row(item));
    }
}

void DialogoEditarConcepto::nuevo()
{
    std::string texto_nuevo_valor = "<nuevo valor>";
    this->termino_sin_editar = texto_nuevo_valor;

    visualizador::modelo::Termino * nuevo_termino = new visualizador::modelo::Termino(texto_nuevo_valor);
    nuevo_termino->sumarReferencia();

    QListWidgetItem* item = new QListWidgetItem();

    item->setText(texto_nuevo_valor.c_str());
    item->setFlags(item->flags() | Qt::ItemIsEditable);
    QVariant data = QVariant::fromValue(nuevo_termino);

    this->ui->lista->blockSignals(true);
    item->setData(Qt::UserRole, data);
    this->ui->lista->blockSignals(false);

    this->ui->lista->insertItem(0, item);

    this->ui->lista->editItem(item);
}

void DialogoEditarConcepto::guardar()
{
    if (false == this->etiquetaModificada() && false == this->listaDeTerminosModificada())
    {
        this->close();
        return;
    }

    if (this->ui->lineedit_etiqueta->text().isEmpty())
    {
        QMessageBox * informacion_etiqueta_vacia = this->crearInformacionEtiquetaVacia();
        informacion_etiqueta_vacia->exec();

        delete informacion_etiqueta_vacia;

        return;
    }

    if (0 == this->ui->lista->count())
    {
        QMessageBox * informacion_lista_de_terminos_vacia = this->crearInformacionListaDeTerminosVacia();
        informacion_lista_de_terminos_vacia->exec();

        delete informacion_lista_de_terminos_vacia;

        return;
    }

    this->concepto_a_editar->setEtiqueta(this->ui->lineedit_etiqueta->text().toStdString());

    std::vector<visualizador::modelo::IEntidad*> entidades_a_almacenar = this->gestor_terminos_de_concepto.getEntidadesAAlmacenar();
    for (std::vector<visualizador::modelo::IEntidad*>::iterator it = entidades_a_almacenar.begin(); it != entidades_a_almacenar.end(); it++)
    {
        this->gestor_terminos->almacenar(*it);

        this->concepto_a_editar->agregarTermino(this->gestor_terminos->clonar<visualizador::modelo::Termino>(*it));
    }

    std::vector<visualizador::modelo::IEntidad*> entidades_a_eliminar = this->gestor_terminos_de_concepto.getEntidadesAEliminar();
    for (std::vector<visualizador::modelo::IEntidad*>::iterator it = entidades_a_eliminar.begin(); it != entidades_a_eliminar.end(); it++)
    {
        visualizador::modelo::Termino * termino_a_sacar = this->gestor_terminos->clonar<visualizador::modelo::Termino>(*it);
        this->concepto_a_editar->sacarTermino(termino_a_sacar);
        delete termino_a_sacar;
    }

    this->accept();
}

void DialogoEditarConcepto::termino_actualizado(QListWidgetItem * item_actualizado)
{
    std::string termino_normalizado = item_actualizado->text().toStdString();

    if (false == this->normalizar(&termino_normalizado)) {
        QMessageBox * informacion_termino_invalido = this->crearInformacionTerminoInvalido();
        informacion_termino_invalido->exec();
        this->ui->lista->blockSignals(true);
        item_actualizado->setText(QString(this->termino_sin_editar.c_str()));
        this->ui->lista->blockSignals(false);
        return;
    }

    visualizador::modelo::Termino * nuevo_termino = new visualizador::modelo::Termino(termino_normalizado);

    if (this->termino_sin_editar == nuevo_termino->getValor())
    {// si se deja el mismo valor, entonces no se hace nada.
        delete nuevo_termino;

        return;
    }

    if (this->gestor_terminos_de_concepto.existe(nuevo_termino))
    {// si ya hay un termino con el mismo valor, entonces se vuelve al valor anterior.
        QMessageBox * informacion_termino_existente = this->crearInformacionTerminoExistente();
        informacion_termino_existente->exec();

        this->ui->lista->blockSignals(true);
        item_actualizado->setText(QString(this->termino_sin_editar.c_str()));
        this->ui->lista->blockSignals(false);

        delete informacion_termino_existente;

        delete nuevo_termino;

        return;
    }
    item_actualizado->setText(QString(termino_normalizado.c_str()));

    visualizador::modelo::Termino * termino_a_agregar_a_lista = NULL;

    visualizador::modelo::Termino * termino_encontrado = this->gestor_terminos->encontrar(nuevo_termino);
    if (NULL != termino_encontrado)
    {// si lo encontro, entonces el termino ya existe. lo que hago es clonarlo y usar el clon, y dsp reemplazarlo en la lista visible.
        termino_a_agregar_a_lista = this->gestor_terminos->clonar<visualizador::modelo::Termino>(termino_encontrado);
        delete nuevo_termino;
    }
    else
    {// si no lo encontro, entonces el termino es nuevo. lo agrego a la lista de terminos a agregar.
        termino_a_agregar_a_lista = nuevo_termino;
    }
    this->gestor_terminos_de_concepto.almacenar(termino_a_agregar_a_lista);
    termino_a_agregar_a_lista->sumarReferencia();

    visualizador::modelo::Termino * termino_lista = item_actualizado->data(Qt::UserRole).value<visualizador::modelo::Termino*>();

    if (NULL != termino_lista && 0 == termino_lista->restarReferencia())
    {// si el item ya tiene asignado un termino, entonces lo elimino.
        delete termino_lista;
    }

    QVariant data = QVariant::fromValue(termino_a_agregar_a_lista);
    this->ui->lista->blockSignals(true);
    item_actualizado->setData(Qt::UserRole, data);
    this->ui->lista->blockSignals(false);
}

void DialogoEditarConcepto::guardar_termino_sin_editar(QListWidgetItem * item_actual, QListWidgetItem * item_previo)
{
    if(NULL != item_actual)
    {
        this->termino_sin_editar = item_actual->text().toStdString();
    }
}

// METODOS INTERNOS

bool DialogoEditarConcepto::normalizar(std::string * termino) {

    std::string termino_original = *termino;

    herramientas::utiles::FuncionesString::eliminar_tildes_utf8(termino);
    herramientas::utiles::FuncionesString::todoMinuscula(*termino);
    herramientas::utiles::FuncionesString::eliminarSignosYPuntuacion(*termino, { "*" });
    herramientas::utiles::FuncionesString::eliminarCaracteresDeControl(*termino);
    herramientas::utiles::FuncionesString::eliminarEspaciosRedundantes(*termino);
    herramientas::utiles::FuncionesString::recortar(termino);

    if (herramientas::utiles::FuncionesString::separar(*termino).size() > 3) {
        *termino = termino_original;
        return false;
    }

    return true;
}

bool DialogoEditarConcepto::etiquetaModificada()
{
    return this->ui->lineedit_etiqueta->text().toStdString() != this->etiqueta_original;
}

bool DialogoEditarConcepto::listaDeTerminosModificada()
{
    return (this->gestor_terminos_de_concepto.getEntidadesAAlmacenar().size() != 0 || this->gestor_terminos_de_concepto.getEntidadesAEliminar().size() != 0);
}

void DialogoEditarConcepto::cargarListaTerminos(visualizador::modelo::Concepto * concepto_a_editar)
{
    std::vector<visualizador::modelo::Termino*> terminos_actuales = concepto_a_editar->getTerminos();

    for (std::vector<visualizador::modelo::Termino*>::iterator it = terminos_actuales.begin(); it != terminos_actuales.end(); it++)
    {
        //(*it)->sumarReferencia();
        std::string texto_termino = (*it)->getValor();
        QListWidgetItem* item = new QListWidgetItem();

        visualizador::modelo::Termino* termino_item = this->gestor_terminos->clonar<visualizador::modelo::Termino>(*it);
        termino_item->sumarReferencia();
        QVariant data = QVariant::fromValue(termino_item);
        item->setData(Qt::UserRole, data);
        item->setText(texto_termino.c_str());
        item->setFlags(item->flags() | Qt::ItemIsEditable);

        this->ui->lista->insertItem(0, item);
    }

    this->gestor_terminos_de_concepto.gestionar<visualizador::modelo::Termino>(terminos_actuales);

    aplicacion::Logger::info(std::to_string(terminos_actuales.size()) + " terminos cargados.");

    this->ui->lista->setSelectionMode(QAbstractItemView::SelectionMode::ExtendedSelection);
}

void DialogoEditarConcepto::descargarListaTerminos()
{
    QListWidgetItem * item = nullptr;

    // elimino los terminos de la lista
    visualizador::modelo::Termino * termino_lista = nullptr;
    unsigned int count = ui->lista->count();
    while (0 != ui->lista->count())
    {
        count = ui->lista->count();

        termino_lista = this->ui->lista->item(0)->data(Qt::UserRole).value<visualizador::modelo::Termino*>();

        if (0 == termino_lista->restarReferencia())
        {
            delete termino_lista;
        }

        item = this->ui->lista->takeItem(0);
        delete item;
    }

    aplicacion::Logger::info(std::to_string(count) + " terminos descargados.");
}

// mensajes

QMessageBox * DialogoEditarConcepto::crearInformacionTerminoExistente()
{
    std::string texto = u8"El trmino que se quiere agregar ya existe.";
    visualizador::aplicacion::comunicacion::Informacion informacion_termino_existente(texto);
    return comunicacion::FabricaMensajes::fabricar(&informacion_termino_existente, this);
}

QMessageBox * DialogoEditarConcepto::crearInformacionTerminoInvalido() {
    std::string texto = u8"Imposible crear trmino con ms de tres palabras. Cada trmino contiene una, dos o tres palabras como mximo.";
    visualizador::aplicacion::comunicacion::Informacion informacion_termino_invalido(texto);
    return comunicacion::FabricaMensajes::fabricar(&informacion_termino_invalido, this);
}

QMessageBox * DialogoEditarConcepto::crearInformacionEtiquetaVacia()
{
    std::string texto = u8"No se asigno la etiqueta. La etiqueta sirve para describir e identificar al concepto en las consultas.";
    visualizador::aplicacion::comunicacion::Informacion informacion_etiqueta_vacia(texto);
    return comunicacion::FabricaMensajes::fabricar(&informacion_etiqueta_vacia, this);
}

QMessageBox * DialogoEditarConcepto::crearInformacionListaDeTerminosVacia()
{
    std::string texto = u8"El concepto no tiene trminos. La lista de trminos no puede estar vaca.";
    visualizador::aplicacion::comunicacion::Informacion informacion_etiqueta_vacia(texto);
    return comunicacion::FabricaMensajes::fabricar(&informacion_etiqueta_vacia, this);
}

void DialogoEditarConcepto::conectar_componentes() {

    QObject::connect(this->ui->btn_nuevo, &QPushButton::released, this, &DialogoEditarConcepto::nuevo);
    QObject::connect(this->ui->btn_eliminar, &QPushButton::released, this, &DialogoEditarConcepto::eliminar);
    QObject::connect(this->ui->btn_guardar, &QPushButton::released, this, &DialogoEditarConcepto::guardar);
    QObject::connect(this->ui->btn_cancelar, &QPushButton::released, this, &DialogoEditarConcepto::close);

    QObject::connect(this->ui->lista, &QListWidget::itemChanged, this, &DialogoEditarConcepto::termino_actualizado);
    QObject::connect(this->ui->lista, &QListWidget::currentItemChanged, this, &DialogoEditarConcepto::guardar_termino_sin_editar);
}<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: indexentrysupplier_common.cxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: kz $ $Date: 2006-01-31 18:37:38 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include <indexentrysupplier_common.hxx>
#include <com/sun/star/i18n/CollatorOptions.hpp>
#include <localedata.hxx>

using namespace ::com::sun::star::uno;
using namespace ::com::sun::star;
using namespace ::rtl;

namespace com { namespace sun { namespace star { namespace i18n {

IndexEntrySupplier_Common::IndexEntrySupplier_Common(const Reference < lang::XMultiServiceFactory >& rxMSF)
{
    implementationName = "com.sun.star.i18n.IndexEntrySupplier_Common";
    collator = new CollatorImpl(rxMSF);
    usePhonetic = sal_False;
}

IndexEntrySupplier_Common::~IndexEntrySupplier_Common()
{
    delete collator;
}

Sequence < lang::Locale > SAL_CALL IndexEntrySupplier_Common::getLocaleList() throw (RuntimeException)
{
    throw RuntimeException();
}

Sequence < OUString > SAL_CALL IndexEntrySupplier_Common::getAlgorithmList( const lang::Locale& rLocale ) throw (RuntimeException)
{
    throw RuntimeException();
}

OUString SAL_CALL IndexEntrySupplier_Common::getPhoneticCandidate( const OUString& rIndexEntry,
    const lang::Locale& rLocale ) throw (RuntimeException)
{
    return OUString();
}

sal_Bool SAL_CALL IndexEntrySupplier_Common::usePhoneticEntry( const lang::Locale& rLocale ) throw (RuntimeException)
{
    throw RuntimeException();
}

sal_Bool SAL_CALL IndexEntrySupplier_Common::loadAlgorithm( const lang::Locale& rLocale,
    const OUString& rAlgorithm, sal_Int32 collatorOptions ) throw (RuntimeException)
{
    usePhonetic = LocaleData().isPhonetic(rLocale, rAlgorithm);
    collator->loadCollatorAlgorithm(rAlgorithm, rLocale, collatorOptions);
    aLocale = rLocale;
    aAlgorithm = rAlgorithm;
    return sal_True;
}

OUString SAL_CALL IndexEntrySupplier_Common::getIndexKey( const OUString& rIndexEntry,
    const OUString& rPhoneticEntry, const lang::Locale& rLocale ) throw (RuntimeException)
{
    return rIndexEntry.copy(0, 1);
}

sal_Int16 SAL_CALL IndexEntrySupplier_Common::compareIndexEntry(
    const OUString& rIndexEntry1, const OUString& rPhoneticEntry1, const lang::Locale& rLocale1,
    const OUString& rIndexEntry2, const OUString& rPhoneticEntry2, const lang::Locale& rLocale2 )
    throw (RuntimeException)
{
    return collator->compareString(rIndexEntry1, rIndexEntry2);
}

OUString SAL_CALL IndexEntrySupplier_Common::getIndexCharacter( const OUString& rIndexEntry,
    const lang::Locale& rLocale, const OUString& rAlgorithm ) throw (RuntimeException)
{
    return rIndexEntry.copy(0, 1);
}

OUString SAL_CALL IndexEntrySupplier_Common::getIndexFollowPageWord( sal_Bool bMorePages,
    const lang::Locale& rLocale ) throw (RuntimeException)
{
    throw RuntimeException();
}

const OUString& SAL_CALL
IndexEntrySupplier_Common::getEntry( const OUString& IndexEntry,
    const OUString& PhoneticEntry, const lang::Locale& rLocale ) throw (RuntimeException)
{
    // The condition for using phonetic entry is:
    // usePhonetic is set for the algorithm;
    // rLocale for phonetic entry is same as aLocale for algorithm,
    // which means Chinese phonetic will not be used for Japanese algorithm;
    // phonetic entry is not blank.
    if (usePhonetic && PhoneticEntry.getLength() > 0 && rLocale.Language == aLocale.Language &&
            rLocale.Country == aLocale.Country && rLocale.Variant == aLocale.Variant)
        return PhoneticEntry;
    else
        return IndexEntry;
}

OUString SAL_CALL
IndexEntrySupplier_Common::getImplementationName() throw( RuntimeException )
{
    return OUString::createFromAscii( implementationName );
}

sal_Bool SAL_CALL
IndexEntrySupplier_Common::supportsService(const OUString& rServiceName) throw( RuntimeException )
{
    return rServiceName.compareToAscii(implementationName) == 0;
}

Sequence< OUString > SAL_CALL
IndexEntrySupplier_Common::getSupportedServiceNames() throw( RuntimeException )
{
    Sequence< OUString > aRet(1);
    aRet[0] = OUString::createFromAscii( implementationName );
    return aRet;
}

} } } }
<commit_msg>INTEGRATION: CWS warnings01 (1.3.14); FILE MERGED 2006/04/21 09:13:23 sb 1.3.14.5: #i53898# Made code compile and/or warning-free again after resync to SRC680m162. 2006/04/20 15:23:15 sb 1.3.14.4: #i53898# Made code compile and/or warning-free again after resync to SRC680m162. 2006/04/07 21:07:34 sb 1.3.14.3: RESYNC: (1.3-1.4); FILE MERGED 2006/03/08 13:19:10 nn 1.3.14.2: #i53898# warning-free code 2005/11/09 20:21:57 pl 1.3.14.1: #i53898# removed warnings<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: indexentrysupplier_common.cxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: hr $ $Date: 2006-06-20 04:45:18 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include <indexentrysupplier_common.hxx>
#include <com/sun/star/i18n/CollatorOptions.hpp>
#include <localedata.hxx>

using namespace ::com::sun::star::uno;
using namespace ::com::sun::star;
using namespace ::rtl;

namespace com { namespace sun { namespace star { namespace i18n {

IndexEntrySupplier_Common::IndexEntrySupplier_Common(const Reference < lang::XMultiServiceFactory >& rxMSF)
{
    implementationName = "com.sun.star.i18n.IndexEntrySupplier_Common";
    collator = new CollatorImpl(rxMSF);
    usePhonetic = sal_False;
}

IndexEntrySupplier_Common::~IndexEntrySupplier_Common()
{
    delete collator;
}

Sequence < lang::Locale > SAL_CALL IndexEntrySupplier_Common::getLocaleList() throw (RuntimeException)
{
    throw RuntimeException();
}

Sequence < OUString > SAL_CALL IndexEntrySupplier_Common::getAlgorithmList( const lang::Locale& ) throw (RuntimeException)
{
    throw RuntimeException();
}

OUString SAL_CALL IndexEntrySupplier_Common::getPhoneticCandidate( const OUString&,
    const lang::Locale& ) throw (RuntimeException)
{
    return OUString();
}

sal_Bool SAL_CALL IndexEntrySupplier_Common::usePhoneticEntry( const lang::Locale& ) throw (RuntimeException)
{
    throw RuntimeException();
}

sal_Bool SAL_CALL IndexEntrySupplier_Common::loadAlgorithm( const lang::Locale& rLocale,
    const OUString& rAlgorithm, sal_Int32 collatorOptions ) throw (RuntimeException)
{
    usePhonetic = LocaleData().isPhonetic(rLocale, rAlgorithm);
    collator->loadCollatorAlgorithm(rAlgorithm, rLocale, collatorOptions);
    aLocale = rLocale;
    aAlgorithm = rAlgorithm;
    return sal_True;
}

OUString SAL_CALL IndexEntrySupplier_Common::getIndexKey( const OUString& rIndexEntry,
    const OUString&, const lang::Locale& ) throw (RuntimeException)
{
    return rIndexEntry.copy(0, 1);
}

sal_Int16 SAL_CALL IndexEntrySupplier_Common::compareIndexEntry(
    const OUString& rIndexEntry1, const OUString&, const lang::Locale&,
    const OUString& rIndexEntry2, const OUString&, const lang::Locale& )
    throw (RuntimeException)
{
    return sal::static_int_cast< sal_Int16 >(
        collator->compareString(rIndexEntry1, rIndexEntry2));
        // return value of compareString in { -1, 0, 1 }
}

OUString SAL_CALL IndexEntrySupplier_Common::getIndexCharacter( const OUString& rIndexEntry,
    const lang::Locale&, const OUString& ) throw (RuntimeException)
{
    return rIndexEntry.copy(0, 1);
}

OUString SAL_CALL IndexEntrySupplier_Common::getIndexFollowPageWord( sal_Bool,
    const lang::Locale& ) throw (RuntimeException)
{
    throw RuntimeException();
}

const OUString& SAL_CALL
IndexEntrySupplier_Common::getEntry( const OUString& IndexEntry,
    const OUString& PhoneticEntry, const lang::Locale& rLocale ) throw (RuntimeException)
{
    // The condition for using phonetic entry is:
    // usePhonetic is set for the algorithm;
    // rLocale for phonetic entry is same as aLocale for algorithm,
    // which means Chinese phonetic will not be used for Japanese algorithm;
    // phonetic entry is not blank.
    if (usePhonetic && PhoneticEntry.getLength() > 0 && rLocale.Language == aLocale.Language &&
            rLocale.Country == aLocale.Country && rLocale.Variant == aLocale.Variant)
        return PhoneticEntry;
    else
        return IndexEntry;
}

OUString SAL_CALL
IndexEntrySupplier_Common::getImplementationName() throw( RuntimeException )
{
    return OUString::createFromAscii( implementationName );
}

sal_Bool SAL_CALL
IndexEntrySupplier_Common::supportsService(const OUString& rServiceName) throw( RuntimeException )
{
    return rServiceName.compareToAscii(implementationName) == 0;
}

Sequence< OUString > SAL_CALL
IndexEntrySupplier_Common::getSupportedServiceNames() throw( RuntimeException )
{
    Sequence< OUString > aRet(1);
    aRet[0] = OUString::createFromAscii( implementationName );
    return aRet;
}

} } } }
<|endoftext|>


<commit_before>
#include "clay.hpp"

#include "llvm/PassManager.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Assembly/PrintModulePass.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/CodeGen/LinkAllAsmWriterComponents.h"
#include "llvm/CodeGen/LinkAllCodegenComponents.h"
#include "llvm/LinkAllVMCore.h"
#include "llvm/Target/TargetData.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/IRReader.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/StandardPasses.h"
#include "llvm/System/Host.h"
#include "llvm/System/Path.h"
#include "llvm/Target/TargetRegistry.h"

#include <iostream>
#include <cstring>

#ifdef WIN32
#define DEFAULT_EXE "a.exe"
#else
#define DEFAULT_EXE "a.out"
#endif

using namespace std;

static void addOptimizationPasses(llvm::PassManager &passes,
                                  llvm::FunctionPassManager &fpasses,
                                  unsigned optLevel)
{
    llvm::createStandardFunctionPasses(&fpasses, optLevel);

    llvm::Pass *inliningPass = 0;
    if (optLevel) {
        unsigned threshold = 200;
        if (optLevel > 2)
            threshold = 250;
        inliningPass = llvm::createFunctionInliningPass(threshold);
    } else {
        inliningPass = llvm::createAlwaysInlinerPass();
    }
    llvm::createStandardModulePasses(&passes,
                                     optLevel,
                                     /*OptimizeSize=*/ false,
                                     /*UnitAtATime=*/ false,
                                     /*UnrollLoops=*/ optLevel > 1,
                                     /*SimplifyLibCalls=*/ true,
                                     /*HaveExceptions=*/ true,
                                     inliningPass);
    llvm::createStandardLTOPasses(&passes,
                                  /*Internalize=*/ true,
                                  /*RunInliner=*/ true,
                                  /*VerifyEach=*/ false);
}

static void optimizeLLVM(llvm::Module *module)
{
    llvm::PassManager passes;

    string moduleDataLayout = module->getDataLayout();
    llvm::TargetData *td = new llvm::TargetData(moduleDataLayout);
    passes.add(td);

    llvm::FunctionPassManager fpasses(module);

    fpasses.add(new llvm::TargetData(*td));

    addOptimizationPasses(passes, fpasses, 3);

    fpasses.doInitialization();
    for (llvm::Module::iterator i = module->begin(), e = module->end();
         i != e; ++i)
    {
        fpasses.run(*i);
    }

    passes.add(llvm::createVerifierPass());

    passes.run(*module);
}

static void generateLLVM(llvm::Module *module, llvm::raw_ostream *out)
{
    llvm::PassManager passes;
    passes.add(llvm::createPrintModulePass(out));
    passes.run(*module);
}

static void generateAssembly(llvm::Module *module, llvm::raw_ostream *out)
{
    llvm::Triple theTriple(module->getTargetTriple());
    string err;
    const llvm::Target *theTarget =
        llvm::TargetRegistry::lookupTarget(theTriple.getTriple(), err);
    assert(theTarget != NULL);
    llvm::TargetMachine *target =
        theTarget->createTargetMachine(theTriple.getTriple(), "");
    assert(target != NULL);

    llvm::CodeGenOpt::Level optLevel = llvm::CodeGenOpt::Aggressive;

    llvm::FunctionPassManager fpasses(module);

    fpasses.add(new llvm::TargetData(module));
    fpasses.add(llvm::createVerifierPass());

    target->setAsmVerbosityDefault(true);

    llvm::formatted_raw_ostream fout(*out);
    bool result =
        target->addPassesToEmitFile(fpasses,
                                    fout,
                                    llvm::TargetMachine::CGFT_AssemblyFile,
                                    optLevel);
    assert(!result);

    fpasses.doInitialization();
    for (llvm::Module::iterator i = module->begin(), e = module->end();
         i != e; ++i)
    {
        fpasses.run(*i);
    }
    fpasses.doFinalization();
}

static bool generateExe(llvm::Module *module,
                        const string &outputFile,
                        const llvm::sys::Path &gccPath)
{
    llvm::sys::Path tempAsm("clayasm");
    string errMsg;
    if (tempAsm.createTemporaryFileOnDisk(false, &errMsg)) {
        cerr << "error: " << errMsg << '\n';
        return false;
    }
    llvm::sys::RemoveFileOnSignal(tempAsm);

    errMsg.clear();
    llvm::raw_fd_ostream asmOut(tempAsm.c_str(),
                                errMsg,
                                llvm::raw_fd_ostream::F_Binary);
    if (!errMsg.empty()) {
        cerr << "error: " << errMsg << '\n';
        return false;
    }

    generateAssembly(module, &asmOut);
    asmOut.close();

    const char *gccArgs[] = {"gcc", "-m32", "-o", outputFile.c_str(),
                             "-x", "assembler", tempAsm.c_str(), NULL};
    llvm::sys::Program::ExecuteAndWait(gccPath, gccArgs);

    if (tempAsm.eraseFromDisk(false, &errMsg)) {
        cerr << "error: " << errMsg << '\n';
        return false;
    }
    return true;
}

static void usage()
{
    cerr << "usage: clay <options> <clayfile>\n";
    cerr << "options:\n";
    cerr << "  -o <file>       - specify output file\n";
    cerr << "  --unoptimized   - generate unoptimized code\n";
    cerr << "  --emit-llvm     - emit llvm code\n";
    cerr << "  --emit-asm      - emit assember code\n";
}

int main(int argc, char **argv) {
    if (argc == 1) {
        usage();
        return -1;
    }

    bool optimize = true;
    bool emitLLVM = false;
    bool emitAsm = false;

    string clayFile;
    string outputFile;

    for (int i = 1; i < argc; ++i) {
        if (strcmp(argv[i], "--unoptimized") == 0) {
            optimize = false;
        }
        else if (strcmp(argv[i], "--emit-llvm") == 0) {
            emitLLVM = true;
        }
        else if (strcmp(argv[i], "--emit-asm") == 0) {
            emitAsm = true;
        }
        else if (strcmp(argv[i], "-o") == 0) {
            if (i+1 == argc) {
                cerr << "error: filename missing after -o\n";
                return -1;
            }
            if (!outputFile.empty()) {
                cerr << "error: output file already specified: "
                     << outputFile
                     << ", specified again as " << argv[i] << '\n';
                return -1;
            }
            ++i;
            outputFile = argv[i];
        }
        else if (strstr(argv[i], "-") != argv[i]) {
            if (!clayFile.empty()) {
                cerr << "error: clay file already specified: " << clayFile
                     << ", unrecognized parameter: " << argv[i] << '\n';
                return -1;
            }
            clayFile = argv[i];
        }
        else {
            cerr << "error: unrecognized option " << argv[i] << '\n';
            return -1;
        }
    }

    if (clayFile.empty()) {
        cerr << "error: clay file not specified\n";
        return -1;
    }

    if (emitLLVM && emitAsm) {
        cerr << "error: use either --emit-llvm or --emit-asm, not both\n";
        return -1;
    }

    llvm::InitializeAllTargets();
    llvm::InitializeAllAsmPrinters();
    initLLVM();
    llvmModule->setTargetTriple(llvm::sys::getHostTriple());

    initTypes();

    llvm::sys::Path clayExe =
        llvm::sys::Path::GetMainExecutable(argv[0], (void *)&main);
    llvm::sys::Path clayDir(clayExe.getDirname());
    llvm::sys::Path libDir(clayDir);
    bool result = libDir.appendComponent("lib-clay");
    assert(result);
    addSearchPath(libDir.str());

    if (outputFile.empty()) {
        if (emitLLVM)
            outputFile = "a.ll";
        else if (emitAsm)
            outputFile = "a.s";
        else
            outputFile = DEFAULT_EXE;
    }
    llvm::sys::RemoveFileOnSignal(llvm::sys::Path(outputFile));

    ModulePtr m = loadProgram(clayFile);
    codegenMain(m);

    if (optimize)
        optimizeLLVM(llvmModule);

    if (emitLLVM || emitAsm) {
        string errorInfo;
        llvm::raw_fd_ostream out(outputFile.c_str(),
                                 errorInfo,
                                 llvm::raw_fd_ostream::F_Binary);
        if (!errorInfo.empty()) {
            cerr << "error: " << errorInfo << '\n';
            return -1;
        }
        if (emitLLVM)
            generateLLVM(llvmModule, &out);
        else if (emitAsm)
            generateAssembly(llvmModule, &out);
    }
    else {
        bool result;
        llvm::sys::Path gccPath;
#ifdef WIN32
        gccPath = clayDir;
        result = gccPath.appendComponent("mingw");
        assert(result);
        result = gccPath.appendComponent("bin");
        assert(result);
        result = gccPath.appendComponent("gcc.exe");
        assert(result);
        if (!gccPath.exists())
            gccPath = llvm::sys::Path();
#endif
        if (!gccPath.isValid()) {
            gccPath = llvm::sys::Program::FindProgramByName("gcc");
        }
        if (!gccPath.isValid()) {
            cerr << "error: unable to find gcc on the path\n";
            return false;
        }

        result = generateExe(llvmModule, outputFile, gccPath);
        if (!result)
            return -1;
    }

    return 0;
}
<commit_msg>when invoking gcc, set argv[0] to full path.<commit_after>
#include "clay.hpp"

#include "llvm/PassManager.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Assembly/PrintModulePass.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/CodeGen/LinkAllAsmWriterComponents.h"
#include "llvm/CodeGen/LinkAllCodegenComponents.h"
#include "llvm/LinkAllVMCore.h"
#include "llvm/Target/TargetData.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/IRReader.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/StandardPasses.h"
#include "llvm/System/Host.h"
#include "llvm/System/Path.h"
#include "llvm/Target/TargetRegistry.h"

#include <iostream>
#include <cstring>

#ifdef WIN32
#define DEFAULT_EXE "a.exe"
#else
#define DEFAULT_EXE "a.out"
#endif

using namespace std;

static void addOptimizationPasses(llvm::PassManager &passes,
                                  llvm::FunctionPassManager &fpasses,
                                  unsigned optLevel)
{
    llvm::createStandardFunctionPasses(&fpasses, optLevel);

    llvm::Pass *inliningPass = 0;
    if (optLevel) {
        unsigned threshold = 200;
        if (optLevel > 2)
            threshold = 250;
        inliningPass = llvm::createFunctionInliningPass(threshold);
    } else {
        inliningPass = llvm::createAlwaysInlinerPass();
    }
    llvm::createStandardModulePasses(&passes,
                                     optLevel,
                                     /*OptimizeSize=*/ false,
                                     /*UnitAtATime=*/ false,
                                     /*UnrollLoops=*/ optLevel > 1,
                                     /*SimplifyLibCalls=*/ true,
                                     /*HaveExceptions=*/ true,
                                     inliningPass);
    llvm::createStandardLTOPasses(&passes,
                                  /*Internalize=*/ true,
                                  /*RunInliner=*/ true,
                                  /*VerifyEach=*/ false);
}

static void optimizeLLVM(llvm::Module *module)
{
    llvm::PassManager passes;

    string moduleDataLayout = module->getDataLayout();
    llvm::TargetData *td = new llvm::TargetData(moduleDataLayout);
    passes.add(td);

    llvm::FunctionPassManager fpasses(module);

    fpasses.add(new llvm::TargetData(*td));

    addOptimizationPasses(passes, fpasses, 3);

    fpasses.doInitialization();
    for (llvm::Module::iterator i = module->begin(), e = module->end();
         i != e; ++i)
    {
        fpasses.run(*i);
    }

    passes.add(llvm::createVerifierPass());

    passes.run(*module);
}

static void generateLLVM(llvm::Module *module, llvm::raw_ostream *out)
{
    llvm::PassManager passes;
    passes.add(llvm::createPrintModulePass(out));
    passes.run(*module);
}

static void generateAssembly(llvm::Module *module, llvm::raw_ostream *out)
{
    llvm::Triple theTriple(module->getTargetTriple());
    string err;
    const llvm::Target *theTarget =
        llvm::TargetRegistry::lookupTarget(theTriple.getTriple(), err);
    assert(theTarget != NULL);
    llvm::TargetMachine *target =
        theTarget->createTargetMachine(theTriple.getTriple(), "");
    assert(target != NULL);

    llvm::CodeGenOpt::Level optLevel = llvm::CodeGenOpt::Aggressive;

    llvm::FunctionPassManager fpasses(module);

    fpasses.add(new llvm::TargetData(module));
    fpasses.add(llvm::createVerifierPass());

    target->setAsmVerbosityDefault(true);

    llvm::formatted_raw_ostream fout(*out);
    bool result =
        target->addPassesToEmitFile(fpasses,
                                    fout,
                                    llvm::TargetMachine::CGFT_AssemblyFile,
                                    optLevel);
    assert(!result);

    fpasses.doInitialization();
    for (llvm::Module::iterator i = module->begin(), e = module->end();
         i != e; ++i)
    {
        fpasses.run(*i);
    }
    fpasses.doFinalization();
}

static bool generateExe(llvm::Module *module,
                        const string &outputFile,
                        const llvm::sys::Path &gccPath)
{
    llvm::sys::Path tempAsm("clayasm");
    string errMsg;
    if (tempAsm.createTemporaryFileOnDisk(false, &errMsg)) {
        cerr << "error: " << errMsg << '\n';
        return false;
    }
    llvm::sys::RemoveFileOnSignal(tempAsm);

    errMsg.clear();
    llvm::raw_fd_ostream asmOut(tempAsm.c_str(),
                                errMsg,
                                llvm::raw_fd_ostream::F_Binary);
    if (!errMsg.empty()) {
        cerr << "error: " << errMsg << '\n';
        return false;
    }

    generateAssembly(module, &asmOut);
    asmOut.close();

    const char *gccArgs[] = {gccPath.c_str(),
                             "-m32",
                             "-o", outputFile.c_str(),
                             "-x", "assembler",
                             tempAsm.c_str(),
                             NULL};
    llvm::sys::Program::ExecuteAndWait(gccPath, gccArgs);

    if (tempAsm.eraseFromDisk(false, &errMsg)) {
        cerr << "error: " << errMsg << '\n';
        return false;
    }
    return true;
}

static void usage()
{
    cerr << "usage: clay <options> <clayfile>\n";
    cerr << "options:\n";
    cerr << "  -o <file>       - specify output file\n";
    cerr << "  --unoptimized   - generate unoptimized code\n";
    cerr << "  --emit-llvm     - emit llvm code\n";
    cerr << "  --emit-asm      - emit assember code\n";
}

int main(int argc, char **argv) {
    if (argc == 1) {
        usage();
        return -1;
    }

    bool optimize = true;
    bool emitLLVM = false;
    bool emitAsm = false;

    string clayFile;
    string outputFile;

    for (int i = 1; i < argc; ++i) {
        if (strcmp(argv[i], "--unoptimized") == 0) {
            optimize = false;
        }
        else if (strcmp(argv[i], "--emit-llvm") == 0) {
            emitLLVM = true;
        }
        else if (strcmp(argv[i], "--emit-asm") == 0) {
            emitAsm = true;
        }
        else if (strcmp(argv[i], "-o") == 0) {
            if (i+1 == argc) {
                cerr << "error: filename missing after -o\n";
                return -1;
            }
            if (!outputFile.empty()) {
                cerr << "error: output file already specified: "
                     << outputFile
                     << ", specified again as " << argv[i] << '\n';
                return -1;
            }
            ++i;
            outputFile = argv[i];
        }
        else if (strstr(argv[i], "-") != argv[i]) {
            if (!clayFile.empty()) {
                cerr << "error: clay file already specified: " << clayFile
                     << ", unrecognized parameter: " << argv[i] << '\n';
                return -1;
            }
            clayFile = argv[i];
        }
        else {
            cerr << "error: unrecognized option " << argv[i] << '\n';
            return -1;
        }
    }

    if (clayFile.empty()) {
        cerr << "error: clay file not specified\n";
        return -1;
    }

    if (emitLLVM && emitAsm) {
        cerr << "error: use either --emit-llvm or --emit-asm, not both\n";
        return -1;
    }

    llvm::InitializeAllTargets();
    llvm::InitializeAllAsmPrinters();
    initLLVM();
    llvmModule->setTargetTriple(llvm::sys::getHostTriple());

    initTypes();

    llvm::sys::Path clayExe =
        llvm::sys::Path::GetMainExecutable(argv[0], (void *)&main);
    llvm::sys::Path clayDir(clayExe.getDirname());
    llvm::sys::Path libDir(clayDir);
    bool result = libDir.appendComponent("lib-clay");
    assert(result);
    addSearchPath(libDir.str());

    if (outputFile.empty()) {
        if (emitLLVM)
            outputFile = "a.ll";
        else if (emitAsm)
            outputFile = "a.s";
        else
            outputFile = DEFAULT_EXE;
    }
    llvm::sys::RemoveFileOnSignal(llvm::sys::Path(outputFile));

    ModulePtr m = loadProgram(clayFile);
    codegenMain(m);

    if (optimize)
        optimizeLLVM(llvmModule);

    if (emitLLVM || emitAsm) {
        string errorInfo;
        llvm::raw_fd_ostream out(outputFile.c_str(),
                                 errorInfo,
                                 llvm::raw_fd_ostream::F_Binary);
        if (!errorInfo.empty()) {
            cerr << "error: " << errorInfo << '\n';
            return -1;
        }
        if (emitLLVM)
            generateLLVM(llvmModule, &out);
        else if (emitAsm)
            generateAssembly(llvmModule, &out);
    }
    else {
        bool result;
        llvm::sys::Path gccPath;
#ifdef WIN32
        gccPath = clayDir;
        result = gccPath.appendComponent("mingw");
        assert(result);
        result = gccPath.appendComponent("bin");
        assert(result);
        result = gccPath.appendComponent("gcc.exe");
        assert(result);
        if (!gccPath.exists())
            gccPath = llvm::sys::Path();
#endif
        if (!gccPath.isValid()) {
            gccPath = llvm::sys::Program::FindProgramByName("gcc");
        }
        if (!gccPath.isValid()) {
            cerr << "error: unable to find gcc on the path\n";
            return false;
        }

        result = generateExe(llvmModule, outputFile, gccPath);
        if (!result)
            return -1;
    }

    return 0;
}
<|endoftext|>


<commit_before>#include <algorithm>
#include <fstream>
#include <iostream>
#include <iterator>
#include <set>
#include <string>
#include <sstream>

#include <cmath>

#include "filtrations/Data.hh"

#include "geometry/RipsExpander.hh"

#include "persistentHomology/ConnectedComponents.hh"

#include "topology/CliqueGraph.hh"
#include "topology/ConnectedComponents.hh"
#include "topology/Simplex.hh"
#include "topology/SimplicialComplex.hh"

#include "topology/io/EdgeLists.hh"
#include "topology/io/GML.hh"

#include "utilities/Filesystem.hh"

using DataType           = double;
using VertexType         = unsigned;
using Simplex            = aleph::topology::Simplex<DataType, VertexType>;
using SimplicialComplex  = aleph::topology::SimplicialComplex<Simplex>;

template <class Simplex> std::string formatSimplex( const Simplex& s )
{
  std::ostringstream stream;
  stream << "[";

  for( auto it = s.begin(); it != s.end(); ++it )
  {
    if( it != s.begin() )
      stream << ",";
    stream << *it;
  }

  stream << "]";

  return stream.str();
}

void usage()
{
  std::cerr << "Usage: clique_communities FILE THRESHOLD K\n"
            << "\n"
            << "Extracts clique communities from FILE, which is supposed to be\n"
            << "a weighted graph. In the subsequent calculation, an edge whose\n"
            << "weight is larger than THRESHOLD will be ignored. K denotes the\n"
            << "maximum dimension of a simplex for the clique graph extraction\n"
            << "and the clique community calculation. This does not correspond\n"
            << "to the dimensionality of the clique. Hence, a parameter of K=2\n"
            << "will result in calculating 3-clique communities because all of\n"
            << "the 2-simplices have 3 vertices.\n\n";
}

int main( int argc, char** argv )
{
  if( argc <= 3 )
  {
    usage();
    return -1;
  }

  std::string filename = argv[1];
  double threshold     = std::stod( argv[2] );
  unsigned maxK        = static_cast<unsigned>( std::stoul( argv[3] ) );

  SimplicialComplex K;

  // Input -------------------------------------------------------------
  //
  // TODO: This is copied from the clique persistence diagram
  // calculation. It would make sense to share some functions
  // between the two applications.

  std::cerr << "* Reading '" << filename << "'...";

  if( aleph::utilities::extension( filename ) == ".gml" )
  {
    aleph::topology::io::GMLReader reader;
    reader( filename, K );
  }
  else
  {
    aleph::io::EdgeListReader reader;
    reader.setReadWeights( true );
    reader.setTrimLines( true );

    reader( filename, K );
  }

  std::cerr << "finished\n";

  // Thresholding ------------------------------------------------------

  {
    std::cerr << "* Filtering input data to threshold epsilon=" << threshold << "...";

    std::vector<Simplex> simplices;

    std::remove_copy_if( K.begin(), K.end(), std::back_inserter( simplices ),
                         [&threshold] ( const Simplex& s )
                         {
                           return s.data() > threshold;
                         } );

    K = SimplicialComplex( simplices.begin(), simplices.end() );

    std::cerr << "finished\n";
  }

  // Expansion ---------------------------------------------------------

  aleph::geometry::RipsExpander<SimplicialComplex> ripsExpander;
  K = ripsExpander( K, maxK );
  K = ripsExpander.assignMaximumWeight( K );

  K.sort( aleph::filtrations::Data<Simplex>() );

  std::cout << "{\n"
            << "  \"" << threshold << "\": {\n";

  for( unsigned k = 1; k <= maxK; k++ )
  {
    std::cerr << "* Extracting " << k << "-cliques graph...";

    auto C
        = aleph::topology::getCliqueGraph( K, k );

    C.sort( aleph::filtrations::Data<Simplex>() );

    std::cerr << "finished\n";

    std::cerr << "* " << k << "-cliques graph has " << C.size() << " simplices\n";

    auto uf = aleph::topology::calculateConnectedComponents( C );

    std::set<VertexType> roots;
    uf.roots( std::inserter( roots, roots.begin() ) );

    std::cerr << "* " << k << "-cliques graph has " << roots.size() << " connected components\n";

    std::cout << "    \"" << (k+1) << "\": [\n";

    for( auto&& root : roots )
    {
      // The vertex IDs stored in the Union--Find data structure
      // correspond to the indices of the simplicial complex. It
      // thus suffices to map them back.
      std::set<VertexType> vertices;
      uf.get( root, std::inserter( vertices, vertices.begin() ) );

      std::vector<Simplex> simplices;

      std::transform( vertices.begin(), vertices.end(), std::back_inserter( simplices ),
                      [&K] ( VertexType v )
                      {
                        return K.at(v);
                      } );

      std::sort( simplices.begin(), simplices.end() );

      std::cout << "            [";

      for( auto it = simplices.begin(); it != simplices.end(); ++it )
      {
        if( it != simplices.begin() )
          std::cout << ",";

        std::cout << formatSimplex( *it );
      }

      std::cout << "            ]";

      if( root != *roots.rbegin() )
        std::cout << ",";

      std::cout << "\n";
    }

    std::cout << "    ]";

    if( k < maxK )
      std::cout << "  ,";

    std::cout << "\n";
  }

  std::cout << "  }\n"
            << "}\n";
}
<commit_msg>Minor cosmetic change in JSON output<commit_after>#include <algorithm>
#include <fstream>
#include <iostream>
#include <iterator>
#include <set>
#include <string>
#include <sstream>

#include <cmath>

#include "filtrations/Data.hh"

#include "geometry/RipsExpander.hh"

#include "persistentHomology/ConnectedComponents.hh"

#include "topology/CliqueGraph.hh"
#include "topology/ConnectedComponents.hh"
#include "topology/Simplex.hh"
#include "topology/SimplicialComplex.hh"

#include "topology/io/EdgeLists.hh"
#include "topology/io/GML.hh"

#include "utilities/Filesystem.hh"

using DataType           = double;
using VertexType         = unsigned;
using Simplex            = aleph::topology::Simplex<DataType, VertexType>;
using SimplicialComplex  = aleph::topology::SimplicialComplex<Simplex>;

template <class Simplex> std::string formatSimplex( const Simplex& s )
{
  std::ostringstream stream;
  stream << "[";

  for( auto it = s.begin(); it != s.end(); ++it )
  {
    if( it != s.begin() )
      stream << ",";
    stream << *it;
  }

  stream << "]";

  return stream.str();
}

void usage()
{
  std::cerr << "Usage: clique_communities FILE THRESHOLD K\n"
            << "\n"
            << "Extracts clique communities from FILE, which is supposed to be\n"
            << "a weighted graph. In the subsequent calculation, an edge whose\n"
            << "weight is larger than THRESHOLD will be ignored. K denotes the\n"
            << "maximum dimension of a simplex for the clique graph extraction\n"
            << "and the clique community calculation. This does not correspond\n"
            << "to the dimensionality of the clique. Hence, a parameter of K=2\n"
            << "will result in calculating 3-clique communities because all of\n"
            << "the 2-simplices have 3 vertices.\n\n";
}

int main( int argc, char** argv )
{
  if( argc <= 3 )
  {
    usage();
    return -1;
  }

  std::string filename = argv[1];
  double threshold     = std::stod( argv[2] );
  unsigned maxK        = static_cast<unsigned>( std::stoul( argv[3] ) );

  SimplicialComplex K;

  // Input -------------------------------------------------------------
  //
  // TODO: This is copied from the clique persistence diagram
  // calculation. It would make sense to share some functions
  // between the two applications.

  std::cerr << "* Reading '" << filename << "'...";

  if( aleph::utilities::extension( filename ) == ".gml" )
  {
    aleph::topology::io::GMLReader reader;
    reader( filename, K );
  }
  else
  {
    aleph::io::EdgeListReader reader;
    reader.setReadWeights( true );
    reader.setTrimLines( true );

    reader( filename, K );
  }

  std::cerr << "finished\n";

  // Thresholding ------------------------------------------------------

  {
    std::cerr << "* Filtering input data to threshold epsilon=" << threshold << "...";

    std::vector<Simplex> simplices;

    std::remove_copy_if( K.begin(), K.end(), std::back_inserter( simplices ),
                         [&threshold] ( const Simplex& s )
                         {
                           return s.data() > threshold;
                         } );

    K = SimplicialComplex( simplices.begin(), simplices.end() );

    std::cerr << "finished\n";
  }

  // Expansion ---------------------------------------------------------

  aleph::geometry::RipsExpander<SimplicialComplex> ripsExpander;
  K = ripsExpander( K, maxK );
  K = ripsExpander.assignMaximumWeight( K );

  K.sort( aleph::filtrations::Data<Simplex>() );

  std::cout << "{\n"
            << "  \"" << threshold << "\": {\n";

  for( unsigned k = 1; k <= maxK; k++ )
  {
    std::cerr << "* Extracting " << k << "-cliques graph...";

    auto C
        = aleph::topology::getCliqueGraph( K, k );

    C.sort( aleph::filtrations::Data<Simplex>() );

    std::cerr << "finished\n";

    std::cerr << "* " << k << "-cliques graph has " << C.size() << " simplices\n";

    auto uf = aleph::topology::calculateConnectedComponents( C );

    std::set<VertexType> roots;
    uf.roots( std::inserter( roots, roots.begin() ) );

    std::cerr << "* " << k << "-cliques graph has " << roots.size() << " connected components\n";

    std::cout << "    \"" << (k+1) << "\": [\n";

    for( auto&& root : roots )
    {
      // The vertex IDs stored in the Union--Find data structure
      // correspond to the indices of the simplicial complex. It
      // thus suffices to map them back.
      std::set<VertexType> vertices;
      uf.get( root, std::inserter( vertices, vertices.begin() ) );

      std::vector<Simplex> simplices;

      std::transform( vertices.begin(), vertices.end(), std::back_inserter( simplices ),
                      [&K] ( VertexType v )
                      {
                        return K.at(v);
                      } );

      std::sort( simplices.begin(), simplices.end() );

      std::cout << "            [";

      for( auto it = simplices.begin(); it != simplices.end(); ++it )
      {
        if( it != simplices.begin() )
          std::cout << ",";

        std::cout << formatSimplex( *it );
      }

      std::cout << "]";

      if( root != *roots.rbegin() )
        std::cout << ",";

      std::cout << "\n";
    }

    std::cout << "    ]";

    if( k < maxK )
      std::cout << "  ,";

    std::cout << "\n";
  }

  std::cout << "  }\n"
            << "}\n";
}
<|endoftext|>