<commit_before>//
// Copyright (c) 2012 Kim Walisch, <kim.walisch@gmail.com>.
// All rights reserved.
//
// This file is part of primesieve.
// Homepage: http://primesieve.googlecode.com
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//   * Redistributions of source code must retain the above copyright
//     notice, this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above
//     copyright notice, this list of conditions and the following
//     disclaimer in the documentation and/or other materials provided
//     with the distribution.
//   * Neither the name of the author nor the names of its
//     contributors may be used to endorse or promote products derived
//     from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.

/**
 * @file  main.cpp
 * @brief Command-line version of primesieve, multi-threaded (OpenMP).
 * @see   http://primesieve.googlecode.com
 *
 * primesieve is a highly optimized implementation of the sieve of
 * Eratosthenes that generates prime numbers and prime k-tuplets (twin
 * primes, prime triplets, ...) up to 2^64 maximum.
 */

#include "../expr/ExpressionParser.h"
#include "../soe/ParallelPrimeSieve.h"

/// declared in test.cpp
void test();

#include <stdint.h>
#include <exception>
#include <iostream>
#include <string>
#include <vector>
#include <iomanip> /* std::setw(int) */

namespace {

std::vector<uint64_t> numbers; /* start and stop number for sieving */

int32_t maxThreads = ParallelPrimeSieve::getMaxThreads();
int32_t threads    = -1;
int32_t flags      =  0;
int32_t sieveSize  = -1;
int32_t preSieve   = -1;

bool quietMode   = false;
bool printParser = false;

const std::string primes[7] = {
  "Prime numbers",
  "Twin primes",
  "Prime triplets",
  "Prime quadruplets",
  "Prime quintuplets",
  "Prime sextuplets",
  "Prime septuplets"
};

enum {
  OPTION_ERROR,
  OPTION_HELP,
  OPTION_TEST,
  OPTION_VERSION,
  START_SIEVING
};

void help() {
  std::cout << "Usage: primesieve START STOP [OPTION]..."                                              << std::endl
            << "Use the segmented sieve of Eratosthenes to generate the prime numbers and/or"          << std::endl
            << "prime k-tuplets in the interval [START, STOP] < 2^64"                                  << std::endl
                                                                                << std::endl
            << "Options:"                                                                              << std::endl
                                                                                << std::endl
            << "  -c<N+>        Count prime numbers and/or prime k-tuplets, 1 <= N <= 7"               << std::endl
            << "                e.g. -c1 count prime numbers (DEFAULT)"                                << std::endl
            << "                     -c23 count twin primes and prime triplets"                        << std::endl
            << "  -o<OFFSET>    Sieve the interval [START, START+OFFSET]"                              << std::endl
            << "  -p<N>         Print prime numbers or prime k-tuplets, 1 <= N <= 7"                   << std::endl
            << "                e.g. -p1 print prime numbers"                                          << std::endl
            << "                     -p5 print prime quintuplets"                                      << std::endl
            << "  -q            Quiet mode, print less output"                                         << std::endl
            << "  -r<PRE-SIEVE> Pre-sieve multiples of small primes <= PRE-SIEVE to speed up"          << std::endl
            << "                the sieve of Eratosthenes, 13 <= PRE-SIEVE <= 23"                      << std::endl
            << "  -s<SIZE>      Set the sieve size in kilobytes, 1 <= SIZE <= 4096"                    << std::endl
            << "                Set SIZE to your CPU's L1/L2 cache size for best performance"          << std::endl
            << "  -t<THREADS>   Set the number of threads for sieving, 1 <= THREADS <= " << maxThreads << std::endl
            << "                Primes are not generated in order if THREADS >= 2"                     << std::endl
            << "  -test         Run various sieving tests and exit"                                    << std::endl
            << "  -v            Print version and license information and exit"                        << std::endl
                                                                                << std::endl
            << "Examples:"                                                                             << std::endl
                                                                                << std::endl
            << "Print the prime numbers up to 1000:"                                                   << std::endl
            << "> primesieve 2 1000 -p1"                                                               << std::endl
                                                                                << std::endl
            << "Count the twin primes and prime triplets in the interval [1E10, 1E10+2^32]:"           << std::endl
            << "> primesieve 1E10 -o2**32 -c23"                                                        << std::endl;
}

void version() {
  std::cout << "primesieve 3.5, <http://primesieve.googlecode.com>" << std::endl
            << "Copyright (C) 2012 Kim Walisch" << std::endl
            << "This software is licensed under the New BSD License. See the LICENSE file" << std::endl
            << "for more information." << std::endl;
}

bool isDigits(const std::string &str) {
  const std::string digits("0123456789");
  if (str.size() == 0)
    return false;
  return str.find_first_not_of(digits) == std::string::npos;
}

/**
 * Process the command-line options.
 * @see help(void)
 */
int processOptions(std::size_t argc, char* argv[]) {
  if (argc < 2 || argc > 20)
    return OPTION_HELP;

  std::string testOption("est");
  std::string arg;
  ExpressionParser<uint64_t> parser;
  uint64_t tmp = 0;

  // process the START and STOP numbers
  for (std::size_t i = 1; i < 3 && i < argc; i++) {
    if (parser.eval(argv[i])) {
      numbers.push_back(parser.getResult());
      if (!isDigits(argv[i]))
        printParser = true;
    }
  }
  // process the options
  for (std::size_t i = numbers.size() + 1; i < argc; i++) {
    if (*argv[i] != '-' && *argv[i] != '/')
      return OPTION_HELP;
    argv[i]++;
    switch (*argv[i]++) {
      case 'c': parser.eval(argv[i]);
                if (!parser.isSuccess())
                  return OPTION_HELP;
                tmp = parser.getResult();
                do {
                  if (tmp % 10 < 1 || tmp % 10 > 7)
                    return OPTION_HELP;
                  flags |= ParallelPrimeSieve::COUNT_PRIMES << (tmp % 10 - 1);
                  tmp /= 10;
                } while (tmp > 0);
                break;
      case 'o': parser.eval(argv[i]);
                if (!parser.isSuccess() || numbers.size() == 0)
                  return OPTION_HELP;
                tmp = numbers[0] + parser.getResult();
                numbers.push_back(tmp);
                break;
      case 'p': parser.eval(argv[i]);
                if (!parser.isSuccess() || parser.getResult() < 1 || parser.getResult() > 7)
                  return OPTION_HELP;
                tmp = parser.getResult() - 1;
                flags |= ParallelPrimeSieve::PRINT_PRIMES << tmp;
                quietMode = true;
                break;
      case 'q': quietMode = true;
                break;
      case 'r': parser.eval(argv[i]);
                if (!parser.isSuccess())
                  return OPTION_HELP;
                preSieve = static_cast<int32_t>(parser.getResult());
                break;
      case 's': parser.eval(argv[i]);
                if (!parser.isSuccess())
                  return OPTION_HELP;
                sieveSize = static_cast<int32_t>(parser.getResult());
                break;
      case 't': arg = argv[i];
                if (arg.compare(testOption) == 0)
                  return OPTION_TEST;
                parser.eval(argv[i]);
                if (!parser.isSuccess())
                  return OPTION_HELP;
                threads = static_cast<int32_t>(parser.getResult());
                break;
      case 'v': return OPTION_VERSION;
      default : return OPTION_HELP;
    }
  }
  return (numbers.size() == 2) ? START_SIEVING : OPTION_HELP;
}

} // end anonymous namespace

int main(int argc, char* argv[]) {
  // process the command-line options, see help()
  switch(processOptions(argc, argv)) {
    case OPTION_ERROR:              return 1;
    case OPTION_HELP:    help();    return 0;
    case OPTION_VERSION: version(); return 0;
    case OPTION_TEST:    test();    return 0;
    case START_SIEVING:  break;
  }

  std::cout << std::left;
  if (!quietMode && printParser) {
    std::cout << std::setw(10) << "START" << " = " << numbers[0] << std::endl;
    std::cout << std::setw(10) << "STOP"  << " = " << numbers[1] << std::endl;
  }
  try {
    ParallelPrimeSieve pps;
    pps.setStart(numbers[0]);
    pps.setStop(numbers[1]);
    if (flags     !=  0) pps.setFlags(flags);
    if (sieveSize != -1) pps.setSieveSize(sieveSize);
    if (preSieve  != -1) pps.setPreSieveLimit(preSieve);
    if (threads   != -1) pps.setNumThreads(threads);

    // set default settings
    if (!pps.testFlags(pps.COUNT_FLAGS | pps.PRINT_PRIMES | pps.PRINT_KTUPLETS))
      pps.addFlags(pps.COUNT_PRIMES);
    if (!pps.testFlags(pps.PRINT_PRIMES | pps.PRINT_KTUPLETS) && !quietMode)
      pps.addFlags(pps.PRINT_STATUS);

    if (!quietMode) {
      if (preSieve != -1)
      std::cout << std::setw(10) << "Pre-sieve"  << " = " << pps.getPreSieveLimit() << std::endl;
      std::cout << std::setw(10) << "Sieve size" << " = " << pps.getSieveSize() << " kilobytes" << std::endl;
      std::cout << std::setw(10) << "Threads"    << " = " << pps.getNumThreads() << std::endl;
    }

    // start sieving primes
    pps.sieve();

    if (pps.isFlag(pps.PRINT_STATUS))
      std::cout << std::endl;
    else if (pps.testFlags(pps.COUNT_FLAGS) && pps.testFlags(pps.PRINT_PRIMES | pps.PRINT_KTUPLETS))
      std::cout << std::endl;

    // get max string size
    std::size_t size = (quietMode) ? 0 : 12;
    for (int i = 0; i < 7; i++) {
      if (pps.isFlag(pps.COUNT_PRIMES << i) && size < primes[i].size())
        size = primes[i].size();
    }
    // print prime count results
    int width = static_cast<int>(size);
    for (int32_t i = 0; i < 7; i++) {
      if (pps.isFlag(pps.COUNT_PRIMES << i))
        std::cout << std::setw(width)
                  << primes[i] << " : " << pps.getCounts(i)
                  << std::endl;
    }
    if (!pps.testFlags(pps.PRINT_PRIMES | pps.PRINT_KTUPLETS)) {
      std::cout << std::setw(width)
                << "Time elapsed" << " : " << pps.getTimeElapsed() << " sec"
                << std::endl;
    }
    if (!quietMode && pps.getNumThreads() >= 64) {
      std::cout << "\nHint: the -q (Quiet mode) option significantly reduces the thread" << std::endl
                << "synchronization overhead when using >= 64 threads." << std::endl;
    }
  }
  catch (std::exception& e) {
    std::cerr << "Error: " << e.what() << std::endl
              << "Try `primesieve -help' for more information." << std::endl;
    return 1;
  }
  return 0;
}
<commit_msg>silenced potential warning<commit_after>//
// Copyright (c) 2012 Kim Walisch, <kim.walisch@gmail.com>.
// All rights reserved.
//
// This file is part of primesieve.
// Homepage: http://primesieve.googlecode.com
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//   * Redistributions of source code must retain the above copyright
//     notice, this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above
//     copyright notice, this list of conditions and the following
//     disclaimer in the documentation and/or other materials provided
//     with the distribution.
//   * Neither the name of the author nor the names of its
//     contributors may be used to endorse or promote products derived
//     from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.

/**
 * @file  main.cpp
 * @brief Command-line version of primesieve, multi-threaded (OpenMP).
 * @see   http://primesieve.googlecode.com
 *
 * primesieve is a highly optimized implementation of the sieve of
 * Eratosthenes that generates prime numbers and prime k-tuplets (twin
 * primes, prime triplets, ...) up to 2^64 maximum.
 */

#include "../expr/ExpressionParser.h"
#include "../soe/ParallelPrimeSieve.h"

/// declared in test.cpp
void test();

#include <stdint.h>
#include <exception>
#include <iostream>
#include <string>
#include <vector>
#include <iomanip> /* std::setw(int) */

namespace {

std::vector<uint64_t> numbers; /* start and stop number for sieving */

int32_t threads    = -1;
int32_t flags      =  0;
int32_t sieveSize  = -1;
int32_t preSieve   = -1;

bool quietMode   = false;
bool printParser = false;

const std::string primes[7] = {
  "Prime numbers",
  "Twin primes",
  "Prime triplets",
  "Prime quadruplets",
  "Prime quintuplets",
  "Prime sextuplets",
  "Prime septuplets"
};

enum {
  OPTION_ERROR,
  OPTION_HELP,
  OPTION_TEST,
  OPTION_VERSION,
  START_SIEVING
};

void help() {
  std::cout << "Usage: primesieve START STOP [OPTION]..."                                              << std::endl
            << "Use the segmented sieve of Eratosthenes to generate the prime numbers and/or"          << std::endl
            << "prime k-tuplets in the interval [START, STOP] < 2^64"                                  << std::endl
                                                                                << std::endl
            << "Options:"                                                                              << std::endl
                                                                                << std::endl
            << "  -c<N+>        Count prime numbers and/or prime k-tuplets, 1 <= N <= 7"               << std::endl
            << "                e.g. -c1 count prime numbers (DEFAULT)"                                << std::endl
            << "                     -c23 count twin primes and prime triplets"                        << std::endl
            << "  -o<OFFSET>    Sieve the interval [START, START+OFFSET]"                              << std::endl
            << "  -p<N>         Print prime numbers or prime k-tuplets, 1 <= N <= 7"                   << std::endl
            << "                e.g. -p1 print prime numbers"                                          << std::endl
            << "                     -p5 print prime quintuplets"                                      << std::endl
            << "  -q            Quiet mode, print less output"                                         << std::endl
            << "  -r<PRE-SIEVE> Pre-sieve multiples of small primes <= PRE-SIEVE to speed up"          << std::endl
            << "                the sieve of Eratosthenes, 13 <= PRE-SIEVE <= 23"                      << std::endl
            << "  -s<SIZE>      Set the sieve size in kilobytes, 1 <= SIZE <= 4096"                    << std::endl
            << "                Set SIZE to your CPU's L1/L2 cache size for best performance"          << std::endl
            << "  -t<THREADS>   Set the number of threads for sieving, 1 <= THREADS <= " << ParallelPrimeSieve::getMaxThreads() << std::endl
            << "                Primes are not generated in order if THREADS >= 2"                     << std::endl
            << "  -test         Run various sieving tests and exit"                                    << std::endl
            << "  -v            Print version and license information and exit"                        << std::endl
                                                                                << std::endl
            << "Examples:"                                                                             << std::endl
                                                                                << std::endl
            << "Print the prime numbers up to 1000:"                                                   << std::endl
            << "> primesieve 2 1000 -p1"                                                               << std::endl
                                                                                << std::endl
            << "Count the twin primes and prime triplets in the interval [1E10, 1E10+2^32]:"           << std::endl
            << "> primesieve 1E10 -o2**32 -c23"                                                        << std::endl;
}

void version() {
  std::cout << "primesieve 3.5, <http://primesieve.googlecode.com>" << std::endl
            << "Copyright (C) 2012 Kim Walisch" << std::endl
            << "This software is licensed under the New BSD License. See the LICENSE file" << std::endl
            << "for more information." << std::endl;
}

bool isDigits(const std::string &str) {
  const std::string digits("0123456789");
  if (str.size() == 0)
    return false;
  return str.find_first_not_of(digits) == std::string::npos;
}

/**
 * Process the command-line options.
 * @see help(void)
 */
int processOptions(std::size_t argc, char* argv[]) {
  if (argc < 2 || argc > 20)
    return OPTION_HELP;

  std::string testOption("est");
  std::string arg;
  ExpressionParser<uint64_t> parser;
  uint64_t tmp = 0;

  // process the START and STOP numbers
  for (std::size_t i = 1; i < 3 && i < argc; i++) {
    if (parser.eval(argv[i])) {
      numbers.push_back(parser.getResult());
      if (!isDigits(argv[i]))
        printParser = true;
    }
  }
  // process the options
  for (std::size_t i = numbers.size() + 1; i < argc; i++) {
    if (*argv[i] != '-' && *argv[i] != '/')
      return OPTION_HELP;
    argv[i]++;
    switch (*argv[i]++) {
      case 'c': parser.eval(argv[i]);
                if (!parser.isSuccess())
                  return OPTION_HELP;
                tmp = parser.getResult();
                do {
                  if (tmp % 10 < 1 || tmp % 10 > 7)
                    return OPTION_HELP;
                  flags |= ParallelPrimeSieve::COUNT_PRIMES << (tmp % 10 - 1);
                  tmp /= 10;
                } while (tmp > 0);
                break;
      case 'o': parser.eval(argv[i]);
                if (!parser.isSuccess() || numbers.size() == 0)
                  return OPTION_HELP;
                tmp = numbers[0] + parser.getResult();
                numbers.push_back(tmp);
                break;
      case 'p': parser.eval(argv[i]);
                if (!parser.isSuccess() || parser.getResult() < 1 || parser.getResult() > 7)
                  return OPTION_HELP;
                tmp = parser.getResult() - 1;
                flags |= ParallelPrimeSieve::PRINT_PRIMES << tmp;
                quietMode = true;
                break;
      case 'q': quietMode = true;
                break;
      case 'r': parser.eval(argv[i]);
                if (!parser.isSuccess())
                  return OPTION_HELP;
                preSieve = static_cast<int32_t>(parser.getResult());
                break;
      case 's': parser.eval(argv[i]);
                if (!parser.isSuccess())
                  return OPTION_HELP;
                sieveSize = static_cast<int32_t>(parser.getResult());
                break;
      case 't': arg = argv[i];
                if (arg.compare(testOption) == 0)
                  return OPTION_TEST;
                parser.eval(argv[i]);
                if (!parser.isSuccess())
                  return OPTION_HELP;
                threads = static_cast<int32_t>(parser.getResult());
                break;
      case 'v': return OPTION_VERSION;
      default : return OPTION_HELP;
    }
  }
  return (numbers.size() == 2) ? START_SIEVING : OPTION_HELP;
}

} // end anonymous namespace

int main(int argc, char* argv[]) {
  // process the command-line options, see help()
  switch(processOptions(argc, argv)) {
    case OPTION_ERROR:              return 1;
    case OPTION_HELP:    help();    return 0;
    case OPTION_VERSION: version(); return 0;
    case OPTION_TEST:    test();    return 0;
    case START_SIEVING:  break;
  }

  std::cout << std::left;
  if (!quietMode && printParser) {
    std::cout << std::setw(10) << "START" << " = " << numbers[0] << std::endl;
    std::cout << std::setw(10) << "STOP"  << " = " << numbers[1] << std::endl;
  }
  try {
    ParallelPrimeSieve pps;
    pps.setStart(numbers[0]);
    pps.setStop(numbers[1]);
    if (flags     !=  0) pps.setFlags(flags);
    if (sieveSize != -1) pps.setSieveSize(sieveSize);
    if (preSieve  != -1) pps.setPreSieveLimit(preSieve);
    if (threads   != -1) pps.setNumThreads(threads);

    // set default settings
    if (!pps.testFlags(pps.COUNT_FLAGS | pps.PRINT_PRIMES | pps.PRINT_KTUPLETS))
      pps.addFlags(pps.COUNT_PRIMES);
    if (!pps.testFlags(pps.PRINT_PRIMES | pps.PRINT_KTUPLETS) && !quietMode)
      pps.addFlags(pps.PRINT_STATUS);

    if (!quietMode) {
      if (preSieve != -1)
      std::cout << std::setw(10) << "Pre-sieve"  << " = " << pps.getPreSieveLimit() << std::endl;
      std::cout << std::setw(10) << "Sieve size" << " = " << pps.getSieveSize() << " kilobytes" << std::endl;
      std::cout << std::setw(10) << "Threads"    << " = " << pps.getNumThreads() << std::endl;
    }

    // start sieving primes
    pps.sieve();

    if (pps.isFlag(pps.PRINT_STATUS))
      std::cout << std::endl;
    else if (pps.testFlags(pps.COUNT_FLAGS) && pps.testFlags(pps.PRINT_PRIMES | pps.PRINT_KTUPLETS))
      std::cout << std::endl;

    // get max string size
    std::size_t size = (quietMode) ? 0 : 12;
    for (int i = 0; i < 7; i++) {
      if (pps.isFlag(pps.COUNT_PRIMES << i) && size < primes[i].size())
        size = primes[i].size();
    }
    // print prime count results
    int width = static_cast<int>(size);
    for (int32_t i = 0; i < 7; i++) {
      if (pps.isFlag(pps.COUNT_PRIMES << i))
        std::cout << std::setw(width)
                  << primes[i] << " : " << pps.getCounts(i)
                  << std::endl;
    }
    if (!pps.testFlags(pps.PRINT_PRIMES | pps.PRINT_KTUPLETS)) {
      std::cout << std::setw(width)
                << "Time elapsed" << " : " << pps.getTimeElapsed() << " sec"
                << std::endl;
    }
    if (!quietMode && pps.getNumThreads() >= 64) {
      std::cout << "\nHint: the -q (Quiet mode) option significantly reduces the thread" << std::endl
                << "synchronization overhead when using >= 64 threads." << std::endl;
    }
  }
  catch (std::exception& e) {
    std::cerr << "Error: " << e.what() << std::endl
              << "Try `primesieve -help' for more information." << std::endl;
    return 1;
  }
  return 0;
}
<|endoftext|>


<commit_before>// Copyright 2014-2015 Project Vogue. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <string>

#include "elang/lir/editor.h"

#include "base/logging.h"
#include "elang/lir/factory.h"
#include "elang/lir/instructions.h"
#include "elang/lir/literals.h"

namespace elang {
namespace lir {

//////////////////////////////////////////////////////////////////////
//
// Editor
//
Editor::Editor(Factory* factory, Function* function)
    : basic_block_(nullptr), factory_(factory), function_(function) {
}

Editor::~Editor() {
  DCHECK(!basic_block_);
}

void Editor::Append(Instruction* new_instruction) {
  DCHECK(!new_instruction->basic_block_);
  DCHECK(!new_instruction->id_);
  DCHECK(basic_block_);
  basic_block_->instructions_.AppendNode(new_instruction);
  new_instruction->id_ = factory()->NextInstructionId();
  new_instruction->basic_block_ = basic_block_;
}

bool Editor::Commit() {
  DCHECK(basic_block_);
#ifndef NDEBUG
  basic_block_ = nullptr;
  return true;
#else
  auto const is_valid = Validate(basic_block_);
  basic_block_ = nullptr;
  return is_valid;
#endif
}

void Editor::Edit(BasicBlock* basic_block) {
  DCHECK(!basic_block_);
  DCHECK_EQ(function(), basic_block->function());
  basic_block_ = basic_block;
  if (basic_block_->instructions().empty())
    return;
  DCHECK(Validate(basic_block_));
}

void Editor::EditNewBasicBlock() {
  Edit(NewBasicBlock(function()->exit_block()));
}

void Editor::InsertBefore(Instruction* new_instruction,
                          Instruction* ref_instruction) {
  if (!ref_instruction) {
    Append(new_instruction);
    return;
  }
  DCHECK(basic_block_);
  DCHECK_EQ(basic_block_, ref_instruction->basic_block());
  DCHECK(!new_instruction->basic_block_);
  DCHECK(!new_instruction->id_);
  basic_block_->instructions_.InsertBefore(new_instruction, ref_instruction);
  new_instruction->id_ = factory()->NextInstructionId();
  new_instruction->basic_block_ = basic_block_;
}

BasicBlock* Editor::NewBasicBlock(BasicBlock* reference) {
  DCHECK(reference);
  DCHECK_EQ(function(), reference->function());
  auto const new_block = factory()->NewBasicBlock();
  new_block->function_ = function();
  new_block->id_ = factory()->NextBasicBlockId();
  // We keep exit block at end of basic block list.
  function()->basic_blocks_.InsertBefore(new_block, reference);
  return new_block;
}

void Editor::Remove(Instruction* old_instruction) {
  DCHECK(basic_block_);
  DCHECK_EQ(basic_block_, old_instruction->basic_block_);
  basic_block_->instructions_.RemoveNode(old_instruction);
  old_instruction->id_ = 0;
  old_instruction->basic_block_ = nullptr;
}

void Editor::SetInput(Instruction* instruction, int index, Value new_value) {
  DCHECK(basic_block_);
  DCHECK_EQ(basic_block_, instruction->basic_block());
  instruction->inputs_[index] = new_value;
}

void Editor::SetJump(BasicBlock* target_block) {
  DCHECK(basic_block_);
  if (auto const last =
          basic_block_->last_instruction()->as<JumpInstruction>()) {
    SetInput(last, 0, target_block->value());
    return;
  }
  auto const instr = factory()->NewJumpInstruction(target_block);
  SetTerminator(instr);
}

void Editor::SetOutput(Instruction* instruction, int index, Value new_value) {
  DCHECK(basic_block_);
  DCHECK_EQ(basic_block_, instruction->basic_block());
  instruction->outputs_[index] = new_value;
}

void Editor::SetReturn() {
  DCHECK(basic_block_);
  if (auto const last = basic_block_->last_instruction()->as<RetInstruction>())
    return;
  SetTerminator(factory()->NewRetInstruction());
}

void Editor::SetTerminator(Instruction* instr) {
  DCHECK(basic_block_);
  DCHECK(!instr->basic_block_);
  DCHECK(instr->IsTerminator());
  auto const last = basic_block_->last_instruction();
  if (last && last->IsTerminator())
    Remove(last);
  Append(instr);
}

bool Editor::Validate(BasicBlock* block) {
  if (!block->id()) {
    DVLOG(0) << *block << " should have id.";
    return false;
  }
  if (!block->function()) {
    DVLOG(0) << *block << " is orphan.";
    return false;
  }
  if (block->instructions().empty()) {
    DVLOG(0) << *block << " is empty.";
    return false;
  }
  auto found_terminator = false;
  for (auto instruction : block->instructions()) {
    // TODO(eval1749) We should call |Validation()| function in ISA.
    if (!instruction->id()) {
      DVLOG(0) << *instruction << " should have an id.";
      return false;
    }
    if (instruction->IsTerminator()) {
      if (found_terminator) {
        DVLOG(0) << *block << " has " << *instruction << " at middle.";
        return false;
      }
      found_terminator = true;
    }
  }
  if (!found_terminator) {
    DVLOG(0) << *block << " should have terminator instruction"
             << " instead of " << *block->last_instruction();
    return false;
  }
  return true;
}

bool Editor::Validate(Function* function) {
  if (function->basic_blocks().empty()) {
    DVLOG(0) << *function << " should have blocks.";
    return false;
  }
  if (!function->entry_block()->first_instruction()->is<EntryInstruction>()) {
    DVLOG(0) << *function << " should have an entry block.";
    return false;
  }
  auto found_exit = false;
  for (auto block : function->basic_blocks()) {
    if (!block->id()) {
      DVLOG(0) << *block << " should have an id.";
      return false;
    }
    if (!Validate(block))
      return false;
    if (block->last_instruction()->is<ExitInstruction>()) {
      if (found_exit) {
        DVLOG(0) << *function << " should have only one exit block.";
        return false;
      }
      found_exit = true;
    }
  }
  if (!found_exit) {
    DVLOG(0) << *function << " should have an exit block.";
    return false;
  }
  return true;
}

}  // namespace lir
}  // namespace elang
<commit_msg>elang/lir: Make |Editor::Append(Instruction*)| insert instruction before terminator instruciton.<commit_after>// Copyright 2014-2015 Project Vogue. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <string>

#include "elang/lir/editor.h"

#include "base/logging.h"
#include "elang/lir/factory.h"
#include "elang/lir/instructions.h"
#include "elang/lir/literals.h"

namespace elang {
namespace lir {

//////////////////////////////////////////////////////////////////////
//
// Editor
//
Editor::Editor(Factory* factory, Function* function)
    : basic_block_(nullptr), factory_(factory), function_(function) {
}

Editor::~Editor() {
  DCHECK(!basic_block_);
}

void Editor::Append(Instruction* new_instruction) {
  DCHECK(!new_instruction->basic_block_);
  DCHECK(!new_instruction->id_);
  DCHECK(basic_block_);
  auto const last = basic_block_->last_instruction();
  if (last && last->IsTerminator())
    basic_block_->instructions_.InsertBefore(new_instruction, last);
  else
    basic_block_->instructions_.AppendNode(new_instruction);
  new_instruction->id_ = factory()->NextInstructionId();
  new_instruction->basic_block_ = basic_block_;
}

bool Editor::Commit() {
  DCHECK(basic_block_);
#ifndef NDEBUG
  basic_block_ = nullptr;
  return true;
#else
  auto const is_valid = Validate(basic_block_);
  basic_block_ = nullptr;
  return is_valid;
#endif
}

void Editor::Edit(BasicBlock* basic_block) {
  DCHECK(!basic_block_);
  DCHECK_EQ(function(), basic_block->function());
  basic_block_ = basic_block;
  if (basic_block_->instructions().empty())
    return;
  DCHECK(Validate(basic_block_));
}

void Editor::EditNewBasicBlock() {
  Edit(NewBasicBlock(function()->exit_block()));
}

void Editor::InsertBefore(Instruction* new_instruction,
                          Instruction* ref_instruction) {
  if (!ref_instruction) {
    Append(new_instruction);
    return;
  }
  DCHECK(basic_block_);
  DCHECK_EQ(basic_block_, ref_instruction->basic_block());
  DCHECK(!new_instruction->basic_block_);
  DCHECK(!new_instruction->id_);
  basic_block_->instructions_.InsertBefore(new_instruction, ref_instruction);
  new_instruction->id_ = factory()->NextInstructionId();
  new_instruction->basic_block_ = basic_block_;
}

BasicBlock* Editor::NewBasicBlock(BasicBlock* reference) {
  DCHECK(reference);
  DCHECK_EQ(function(), reference->function());
  auto const new_block = factory()->NewBasicBlock();
  new_block->function_ = function();
  new_block->id_ = factory()->NextBasicBlockId();
  // We keep exit block at end of basic block list.
  function()->basic_blocks_.InsertBefore(new_block, reference);
  return new_block;
}

void Editor::Remove(Instruction* old_instruction) {
  DCHECK(basic_block_);
  DCHECK_EQ(basic_block_, old_instruction->basic_block_);
  basic_block_->instructions_.RemoveNode(old_instruction);
  old_instruction->id_ = 0;
  old_instruction->basic_block_ = nullptr;
}

void Editor::SetInput(Instruction* instruction, int index, Value new_value) {
  DCHECK(basic_block_);
  DCHECK_EQ(basic_block_, instruction->basic_block());
  instruction->inputs_[index] = new_value;
}

void Editor::SetJump(BasicBlock* target_block) {
  DCHECK(basic_block_);
  if (auto const last =
          basic_block_->last_instruction()->as<JumpInstruction>()) {
    SetInput(last, 0, target_block->value());
    return;
  }
  auto const instr = factory()->NewJumpInstruction(target_block);
  SetTerminator(instr);
}

void Editor::SetOutput(Instruction* instruction, int index, Value new_value) {
  DCHECK(basic_block_);
  DCHECK_EQ(basic_block_, instruction->basic_block());
  instruction->outputs_[index] = new_value;
}

void Editor::SetReturn() {
  DCHECK(basic_block_);
  if (auto const last = basic_block_->last_instruction()->as<RetInstruction>())
    return;
  SetTerminator(factory()->NewRetInstruction());
}

void Editor::SetTerminator(Instruction* instr) {
  DCHECK(basic_block_);
  DCHECK(!instr->basic_block_);
  DCHECK(instr->IsTerminator());
  auto const last = basic_block_->last_instruction();
  if (last && last->IsTerminator())
    Remove(last);
  Append(instr);
}

bool Editor::Validate(BasicBlock* block) {
  if (!block->id()) {
    DVLOG(0) << *block << " should have id.";
    return false;
  }
  if (!block->function()) {
    DVLOG(0) << *block << " is orphan.";
    return false;
  }
  if (block->instructions().empty()) {
    DVLOG(0) << *block << " is empty.";
    return false;
  }
  auto found_terminator = false;
  for (auto instruction : block->instructions()) {
    // TODO(eval1749) We should call |Validation()| function in ISA.
    if (!instruction->id()) {
      DVLOG(0) << *instruction << " should have an id.";
      return false;
    }
    if (instruction->IsTerminator()) {
      if (found_terminator) {
        DVLOG(0) << *block << " has " << *instruction << " at middle.";
        return false;
      }
      found_terminator = true;
    }
  }
  if (!found_terminator) {
    DVLOG(0) << *block << " should have terminator instruction"
             << " instead of " << *block->last_instruction();
    return false;
  }
  return true;
}

bool Editor::Validate(Function* function) {
  if (function->basic_blocks().empty()) {
    DVLOG(0) << *function << " should have blocks.";
    return false;
  }
  if (!function->entry_block()->first_instruction()->is<EntryInstruction>()) {
    DVLOG(0) << *function << " should have an entry block.";
    return false;
  }
  auto found_exit = false;
  for (auto block : function->basic_blocks()) {
    if (!block->id()) {
      DVLOG(0) << *block << " should have an id.";
      return false;
    }
    if (!Validate(block))
      return false;
    if (block->last_instruction()->is<ExitInstruction>()) {
      if (found_exit) {
        DVLOG(0) << *function << " should have only one exit block.";
        return false;
      }
      found_exit = true;
    }
  }
  if (!found_exit) {
    DVLOG(0) << *function << " should have an exit block.";
    return false;
  }
  return true;
}

}  // namespace lir
}  // namespace elang
<|endoftext|>


<commit_before>#include <stdio.h>
#include <stdlib.h>

#include <string>
#include <iostream>
#include <utility>
#include <unordered_set>
#include <unordered_map>
using namespace std;

#include "scx/Dir.hpp"
#include "scx/FileInfo.hpp"
using namespace scx;

#include "Tools.h"
#include "ConsUnit.h"
#include "Parallel.h"

static const unordered_set<string> C_EXT = { "c" };
static const unordered_set<string> CXX_EXT = { "cc", "cxx", "cpp", "C" };
static const unordered_set<string> ASM_EXT = { "s", "S", "asm", "nas" };

static void Usage(const string& cmd)
{
    const string sp(cmd.size(), ' ');
    cout << ""
        "Usage:\n"
        "\t" + cmd + " [ file1 file2 ... | dir1 dir2 ... ]\n"
        "\t" + sp + " as=?        Assembler.\n"
        "\t" + sp + " asflag=?    Assembler flag.\n"
        "\t" + sp + " cc=?        C compiler.\n"
        "\t" + sp + " ccflag=?    C Compiler flag.\n"
        "\t" + sp + " cxx=?       C++ compiler.\n"
        "\t" + sp + " cxxflag=?   C++ Compiler flag.\n"
        "\t" + sp + " flag=?      Compiler Common flag.\n"
        "\t" + sp + " ld=?        Linker.\n"
        "\t" + sp + " ldflag=?    Linker flag.\n"
        "\t" + sp + " ldfirst=?   First object file.\n"
        "\t" + sp + " jobs=?      Parallel build.\n"
        "\t" + sp + " workdir=?   Work direcotry.\n"
        "\t" + sp + " out=?       Binary output file name.\n"
        "\t" + sp + " prefix=?    Search head file and library in.\n"
        "\t" + sp + " nlink       Do not link.\n"
        "\t" + sp + " verbose     Verbose output.\n"
        "\t" + sp + " clean       Clean build output.\n"
        "\t" + sp + " help        Show this help message.\n"
        "\n"
        "\t" + sp + " shared      Generate shared library. (*)\n"
        "\t" + sp + " g           -g (*)\n"
        "\t" + sp + " c++11       -std=c++11 (*)\n"
        "\t" + sp + " c++14       -std=c++14 (*)\n"
        "\t" + sp + " c++1y       -std=c++1y (*)\n"
        "\t" + sp + " c++1z       -std=c++1z (*)\n"
        "\t" + sp + " thread      Link against pthread. (*)\n"
        "\n";

    cout << ""
        "Note:\n"
        "\t* Just for convenient. You may also use flags to approach the function.\n"
        "\n";

    cout << ""
        "Author:\n"
        "\tYanhui Shen <@bsdelf on Twitter>\n";
}

int main(int argc, char** argv)
{
    unordered_map<string, string> ArgTable = {
        {   "as",       "as"    },
        {   "asflag",   ""      },
        {   "cc",       "cc"    },
        {   "ccflag",   ""      },
        {   "cxx",      "c++"   },
        {   "cxxflag",  ""      },
        {   "flag",     ""      },
        {   "ld",       "cc"    },
        {   "ldflag",   ""      },
        {   "ldfirst",  ""      },
        {   "jobs",     "0"     },
        {   "workdir",  ""      },
        {   "out",      "b.out" }
    };

    bool clean = false;
    bool nlink = false;
    bool verbose = false;
    vector<string> allsrc;
    vector<string> prefixes;

    // Parse arguments.
    {
        bool useShared = false;
        bool useDebug = false;
        bool usePipe = true;
        bool useC89 = false;
        bool useC99 = true;
        bool useCXX11 = false;
        bool useCXX14 = false;
        bool useCXX1y = false;
        bool useCXX1z = false;
        bool useThread = false;

        for (int i = 1; i < argc; ++i) {
            const string& arg(argv[i]);

            // So obvisously, file name should not contain '='.
            size_t pos = arg.find('=');
            if (pos != string::npos && pos != arg.size()-1) {
                const auto& key = arg.substr(0, pos);
                const auto& val = arg.substr(pos+1);
                // Update key-value.
                auto iter = ArgTable.find(key);
                if (iter != ArgTable.end()) {
                    iter->second = val;
                } else if (key == "prefix") {
                    prefixes.push_back(val);
                } else {
                    cout << "Argument ignored!" << endl;
                    cout << "    Argument: " << arg << endl;
                }
            } else if (arg == "help") {
                Usage(argv[0]);
                return 0;
            } else if (arg == "verbose") {
                verbose = true;
            } else if (arg == "nlink") {
                nlink = true;
            } else if (arg == "clean") {
                clean = true;
            } else if (arg == "g") {
                useDebug = true;
            } else if (arg == "shared") {
                useShared = true;
            } else if (arg == "c89") {
                useC89 = true;
            } else if (arg == "c99") {
                useC99 = true;
            } else if (arg == "c++11") {
                useCXX11 = true;
            } else if (arg == "c++14") {
                useCXX14 = true;
            } else if (arg == "c++1y") {
                useCXX1y = true;
            } else if (arg == "c++1z") {
                useCXX1z = true;
            } else if (arg == "thread") {
                useThread = true;
            } else {
                // Add source files
                switch (FileInfo(arg).Type()) {
                case FileType::Directory:
                    {
                        const auto& files = Dir::ListDir(arg);
                        allsrc.reserve(allsrc.size() + files.size());
                        allsrc.insert(allsrc.end(), files.begin(), files.end());
                    }
                    break;

                case FileType::Regular:
                    {
                        allsrc.push_back(arg);
                    }
                    break;

                default:
                    {
                        cerr << "FATAL: bad argument: " << endl;
                        cerr << "arg: " << arg << endl;
                        return -1;
                    }
                    break;
                }
            }
        }

        if (!ArgTable["workdir"].empty()) {
            auto& dir = ArgTable["workdir"];
            if (dir[dir.size()-1] != '/')
                dir += "/";
            const auto& info = FileInfo(dir);
            if (!info.Exists()) {
                if (!Dir::MakeDir(dir, 0744)) {
                    cerr << "Failed to create directory!" << endl;
                    cerr << "    Directory: " << dir << endl;
                    return -1;
                }
            } else if (info.Type() != FileType::Directory) {
                cerr << "Bad work directory! " << endl;
                cerr << "    Directory: " << dir << endl;
                cerr << "    File type: " << FileType::ToString(info.Type()) << endl;
                return -1;
            }
        }

        //-------------------------------------------------------------
        // additional compiler common flag

        if (!ArgTable["flag"].empty()) {
            ArgTable["flag"].insert(0, 1, ' ');
        }

        for (const auto& prefix: prefixes) {
            ArgTable["flag"] += " -I " + prefix + "/include";
            ArgTable["ldflag"] += " -L " + prefix + "/lib";
        }

        if (useDebug) {
            ArgTable["flag"] += " -g";
        }

        if (usePipe) {
            ArgTable["flag"] += " -pipe";
        }

        if (useShared) {
            ArgTable["flag"] += " -fPIC";
            ArgTable["ldflag"] += " -shared";
        }

        //-------------------------------------------------------------
        // additional link flag

        if (!ArgTable["ldflag"].empty()) {
            ArgTable["ldflag"].insert(0, 1, ' ');
        }

        if (useThread) {
            ArgTable["ldflag"] += " -pthread";
        }

        //-------------------------------------------------------------
        // additional c/c++ compiler flag

        if (ArgTable["ccflag"].empty()) {
            ArgTable["ccflag"] = ArgTable["flag"];
        } else {
            ArgTable["ccflag"].insert(0, ArgTable["flag"] + " ");
        }

        if (useC89) {
            ArgTable["ccflag"] += " -std=c89";
        } else if (useC99) {
            ArgTable["ccflag"] += " -std=c99";
        }

        if (ArgTable["cxxflag"].empty()) {
            ArgTable["cxxflag"] = ArgTable["flag"];
        } else {
            ArgTable["cxxflag"].insert(0, ArgTable["flag"] + " ");
        }

        if (useCXX11) {
            ArgTable["cxxflag"] += " -std=c++11";
        } else if (useCXX14) {
            ArgTable["cxxflag"] += " -std=c++14";
        } else if (useCXX1y) {
            ArgTable["cxxflag"] += " -std=c++1y";
        } else if (useCXX1z) {
            ArgTable["cxxflag"] += " -std=c++1z";
        }

        //-------------------------------------------------------------

        // if unspecified, take all files under current folder
        if (allsrc.empty()) {
            allsrc = Dir::ListDir(".");
        }

        if (allsrc.empty()) {
            cerr << "FATAL: nothing to build!" << endl;
            return -1;
        }
    }

    // Prepare construct units.
    // Note: "workdir" & "out" should occur together
    bool hasCpp = false;
    bool hasOut = FileInfo(ArgTable["workdir"] + ArgTable["out"]).Exists();
    vector<ConsUnit> newUnits;
    string allObjects;

    for (const auto& file: allsrc) {
        ConsUnit unit(ArgTable["workdir"], file);
        string compiler;
        string compilerFlag;

        // Calculate dependence.
        bool ok = false;
        const string ext(FileInfo(file).Suffix());
        if (C_EXT.find(ext) != C_EXT.end()) {
            compiler = ArgTable["cc"];
            compilerFlag = ArgTable["ccflag"];
            ok = ConsUnit::InitC(unit, compiler, compilerFlag);
        } else if (CXX_EXT.find(ext) != CXX_EXT.end()) {
            hasCpp = true;
            compiler = ArgTable["cxx"];
            compilerFlag = ArgTable["cxxflag"];
            ok = ConsUnit::InitCpp(unit, compiler, compilerFlag);
        } else if (ASM_EXT.find(ext) != ASM_EXT.end()) {
            compiler = ArgTable["as"];
            compilerFlag = ArgTable["asflag"];
            ok = ConsUnit::InitAsm(unit, compiler, compilerFlag);
        } else {
            continue;
        }

        if (ok) {
            if (unit.out == ArgTable["ldfirst"])
                allObjects = " " + unit.out + allObjects;
            else
                allObjects += " " + unit.out;
            if (!unit.cmd.empty())
                newUnits.push_back(std::move(unit));
        } else {
            cerr << "FATAL: failed to calculate dependence!" << endl;
            cerr << "    file:     " << file << endl;
            cerr << "    compiler: " << compiler << endl;
            cerr << "    flag:     " << ArgTable["flag"] << endl;
            cerr << "    ccflag:   " << ArgTable["ccflag"] << endl;
            cerr << "    cxxflag:  " << ArgTable["cxxflag"] << endl;
            return -1;
        }
    }

    if (hasCpp) {
        ArgTable["ld"] = ArgTable["cxx"];
    }

#ifdef DEBUG
    // Debug info.
    {
        auto fn = [](const vector<ConsUnit>& units)->void {
            for (const auto& unit: units) {
                cout << "in: " <<  unit.in << ", " << "out: " << unit.out << endl;
                cout << "\t" << unit.cmd << endl;
                cout << "\t";
                for (const auto& dep: unit.deps)
                    cout << dep << ", ";
                cout << endl;
            }
        };
        cout << "new units: " << endl;
        fn(newUnits);
    }
#endif

    // clean
    if (clean) {
        const string& cmd = "rm -f " + ArgTable["workdir"] + ArgTable["out"] + allObjects;
        cout << cmd << endl;
        ::system(cmd.c_str());
        return 0;
    }

    // compile
    if (!newUnits.empty()) {
        cout << "* Build: ";
        if (!verbose) {
            cout << newUnits.size() << (newUnits.size() > 1 ? " files" : " file");
        } else {
            for (size_t i = 0; i < newUnits.size(); ++i) {
                cout << newUnits[i].in << ((i+1 < newUnits.size()) ? ", " : "");
            }
        }
        cout << endl;

        ParallelCompiler pc(newUnits);
        pc.SetVerbose(verbose);
        if (pc.Run(std::stoi(ArgTable["jobs"])) != 0)
            return -1;
    }

    // link
    if ((!hasOut || !newUnits.empty()) && !nlink) {
        string ldCmd = ArgTable["ld"] + ArgTable["ldflag"];
        ldCmd += " -o " + ArgTable["workdir"] + ArgTable["out"] + allObjects;

        if (!verbose)
            cout << "- Link - " << ArgTable["workdir"] + ArgTable["out"] << endl;
        else
            cout << "- Link - " << ldCmd << endl;
        if (::system(ldCmd.c_str()) != 0) {
            cerr << "FATAL: failed to link!" << endl;
            cerr << "    file:   " << allObjects << endl;
            cerr << "    ld:     " << ArgTable["ld"] << endl;
            cerr << "    ldflag: " << ArgTable["ldflag"] << endl;
            return -2;
        }
    }

    return 0;
}
<commit_msg>* support "release" version<commit_after>#include <stdio.h>
#include <stdlib.h>

#include <string>
#include <iostream>
#include <utility>
#include <unordered_set>
#include <unordered_map>
using namespace std;

#include "scx/Dir.hpp"
#include "scx/FileInfo.hpp"
using namespace scx;

#include "Tools.h"
#include "ConsUnit.h"
#include "Parallel.h"

static const unordered_set<string> C_EXT = { "c" };
static const unordered_set<string> CXX_EXT = { "cc", "cxx", "cpp", "C" };
static const unordered_set<string> ASM_EXT = { "s", "S", "asm", "nas" };

static void Usage(const string& cmd)
{
    const string sp(cmd.size(), ' ');
    cout << ""
        "Usage:\n"
        "\t" + cmd + " [ file1 file2 ... | dir1 dir2 ... ]\n"
        "\t" + sp + " as=?        Assembler.\n"
        "\t" + sp + " asflag=?    Assembler flag.\n"
        "\t" + sp + " cc=?        C compiler.\n"
        "\t" + sp + " ccflag=?    C Compiler flag.\n"
        "\t" + sp + " cxx=?       C++ compiler.\n"
        "\t" + sp + " cxxflag=?   C++ Compiler flag.\n"
        "\t" + sp + " flag=?      Compiler Common flag.\n"
        "\t" + sp + " ld=?        Linker.\n"
        "\t" + sp + " ldflag=?    Linker flag.\n"
        "\t" + sp + " ldfirst=?   First object file.\n"
        "\t" + sp + " jobs=?      Parallel build.\n"
        "\t" + sp + " workdir=?   Work direcotry.\n"
        "\t" + sp + " out=?       Binary output file name.\n"
        "\t" + sp + " prefix=?    Search head file and library in.\n"
        "\t" + sp + " nlink       Do not link.\n"
        "\t" + sp + " verbose     Verbose output.\n"
        "\t" + sp + " clean       Clean build output.\n"
        "\t" + sp + " help        Show this help message.\n"
        "\n"
        "\t" + sp + " thread      Link against pthread. (*)\n"
        "\t" + sp + " shared      Generate shared library. (*)\n"
        "\n"
        "\t" + sp + " release     -DNDEBUG (*)\n"
        "\t" + sp + " debug       -g (*)\n"
        "\t" + sp + " c++11       -std=c++11 (*)\n"
        "\t" + sp + " c++14       -std=c++14 (*)\n"
        "\t" + sp + " c++1y       -std=c++1y (*)\n"
        "\t" + sp + " c++1z       -std=c++1z (*)\n"
        "\n";

    cout << ""
        "Note:\n"
        "\t* Just for convenient. You may also use flags to approach the function.\n"
        "\n";

    cout << ""
        "Author:\n"
        "\tYanhui Shen <@bsdelf on Twitter>\n";
}

int main(int argc, char** argv)
{
    unordered_map<string, string> ArgTable = {
        {   "as",       "as"    },
        {   "asflag",   ""      },
        {   "cc",       "cc"    },
        {   "ccflag",   ""      },
        {   "cxx",      "c++"   },
        {   "cxxflag",  ""      },
        {   "flag",     ""      },
        {   "ld",       "cc"    },
        {   "ldflag",   ""      },
        {   "ldfirst",  ""      },
        {   "jobs",     "0"     },
        {   "workdir",  ""      },
        {   "out",      "b.out" }
    };

    bool clean = false;
    bool nlink = false;
    bool verbose = false;
    vector<string> allsrc;
    vector<string> prefixes;

    // Parse arguments.
    {
        bool useThread = false;
        bool useShared = false;
        bool useRelease = false;
        bool useDebug = false;
        bool usePipe = true;
        bool useC89 = false;
        bool useC99 = true;
        bool useCXX11 = false;
        bool useCXX14 = false;
        bool useCXX1y = false;
        bool useCXX1z = false;

        for (int i = 1; i < argc; ++i) {
            const string& arg(argv[i]);

            // So obvisously, file name should not contain '='.
            size_t pos = arg.find('=');
            if (pos != string::npos && pos != arg.size()-1) {
                const auto& key = arg.substr(0, pos);
                const auto& val = arg.substr(pos+1);
                // Update key-value.
                auto iter = ArgTable.find(key);
                if (iter != ArgTable.end()) {
                    iter->second = val;
                } else if (key == "prefix") {
                    prefixes.push_back(val);
                } else {
                    cout << "Argument ignored!" << endl;
                    cout << "    Argument: " << arg << endl;
                }
            } else if (arg == "help") {
                Usage(argv[0]);
                return 0;
            } else if (arg == "verbose") {
                verbose = true;
            } else if (arg == "nlink") {
                nlink = true;
            } else if (arg == "clean") {
                clean = true;
            } else if (arg == "thread") {
                useThread = true;
            } else if (arg == "release") {
                useRelease = true;
            } else if (arg == "debug") {
                useDebug = true;
            } else if (arg == "shared") {
                useShared = true;
            } else if (arg == "c89") {
                useC89 = true;
            } else if (arg == "c99") {
                useC99 = true;
            } else if (arg == "c++11") {
                useCXX11 = true;
            } else if (arg == "c++14") {
                useCXX14 = true;
            } else if (arg == "c++1y") {
                useCXX1y = true;
            } else if (arg == "c++1z") {
                useCXX1z = true;
            } else {
                // Add source files
                switch (FileInfo(arg).Type()) {
                case FileType::Directory:
                    {
                        const auto& files = Dir::ListDir(arg);
                        allsrc.reserve(allsrc.size() + files.size());
                        allsrc.insert(allsrc.end(), files.begin(), files.end());
                    }
                    break;

                case FileType::Regular:
                    {
                        allsrc.push_back(arg);
                    }
                    break;

                default:
                    {
                        cerr << "FATAL: bad argument: " << endl;
                        cerr << "arg: " << arg << endl;
                        return -1;
                    }
                    break;
                }
            }
        }

        if (!ArgTable["workdir"].empty()) {
            auto& dir = ArgTable["workdir"];
            if (dir[dir.size()-1] != '/')
                dir += "/";
            const auto& info = FileInfo(dir);
            if (!info.Exists()) {
                if (!Dir::MakeDir(dir, 0744)) {
                    cerr << "Failed to create directory!" << endl;
                    cerr << "    Directory: " << dir << endl;
                    return -1;
                }
            } else if (info.Type() != FileType::Directory) {
                cerr << "Bad work directory! " << endl;
                cerr << "    Directory: " << dir << endl;
                cerr << "    File type: " << FileType::ToString(info.Type()) << endl;
                return -1;
            }
        }

        //-------------------------------------------------------------
        // additional compiler common flag

        if (!ArgTable["flag"].empty()) {
            ArgTable["flag"].insert(0, 1, ' ');
        }

        for (const auto& prefix: prefixes) {
            ArgTable["flag"] += " -I " + prefix + "/include";
            ArgTable["ldflag"] += " -L " + prefix + "/lib";
        }

        if (useRelease) {
            ArgTable["flag"] += " -DNDEBUG";
        } else if (useDebug) {
            ArgTable["flag"] += " -g";
        }

        if (usePipe) {
            ArgTable["flag"] += " -pipe";
        }

        if (useShared) {
            ArgTable["flag"] += " -fPIC";
            ArgTable["ldflag"] += " -shared";
        }

        //-------------------------------------------------------------
        // additional link flag

        if (!ArgTable["ldflag"].empty()) {
            ArgTable["ldflag"].insert(0, 1, ' ');
        }

        if (useThread) {
            ArgTable["ldflag"] += " -pthread";
        }

        //-------------------------------------------------------------
        // additional c/c++ compiler flag

        if (ArgTable["ccflag"].empty()) {
            ArgTable["ccflag"] = ArgTable["flag"];
        } else {
            ArgTable["ccflag"].insert(0, ArgTable["flag"] + " ");
        }

        if (useC89) {
            ArgTable["ccflag"] += " -std=c89";
        } else if (useC99) {
            ArgTable["ccflag"] += " -std=c99";
        }

        if (ArgTable["cxxflag"].empty()) {
            ArgTable["cxxflag"] = ArgTable["flag"];
        } else {
            ArgTable["cxxflag"].insert(0, ArgTable["flag"] + " ");
        }

        if (useCXX11) {
            ArgTable["cxxflag"] += " -std=c++11";
        } else if (useCXX14) {
            ArgTable["cxxflag"] += " -std=c++14";
        } else if (useCXX1y) {
            ArgTable["cxxflag"] += " -std=c++1y";
        } else if (useCXX1z) {
            ArgTable["cxxflag"] += " -std=c++1z";
        }

        //-------------------------------------------------------------

        // if unspecified, take all files under current folder
        if (allsrc.empty()) {
            allsrc = Dir::ListDir(".");
        }

        if (allsrc.empty()) {
            cerr << "FATAL: nothing to build!" << endl;
            return -1;
        }
    }

    // Prepare construct units.
    // Note: "workdir" & "out" should occur together
    bool hasCpp = false;
    bool hasOut = FileInfo(ArgTable["workdir"] + ArgTable["out"]).Exists();
    vector<ConsUnit> newUnits;
    string allObjects;

    for (const auto& file: allsrc) {
        ConsUnit unit(ArgTable["workdir"], file);
        string compiler;
        string compilerFlag;

        // Calculate dependence.
        bool ok = false;
        const string ext(FileInfo(file).Suffix());
        if (C_EXT.find(ext) != C_EXT.end()) {
            compiler = ArgTable["cc"];
            compilerFlag = ArgTable["ccflag"];
            ok = ConsUnit::InitC(unit, compiler, compilerFlag);
        } else if (CXX_EXT.find(ext) != CXX_EXT.end()) {
            hasCpp = true;
            compiler = ArgTable["cxx"];
            compilerFlag = ArgTable["cxxflag"];
            ok = ConsUnit::InitCpp(unit, compiler, compilerFlag);
        } else if (ASM_EXT.find(ext) != ASM_EXT.end()) {
            compiler = ArgTable["as"];
            compilerFlag = ArgTable["asflag"];
            ok = ConsUnit::InitAsm(unit, compiler, compilerFlag);
        } else {
            continue;
        }

        if (ok) {
            if (unit.out == ArgTable["ldfirst"])
                allObjects = " " + unit.out + allObjects;
            else
                allObjects += " " + unit.out;
            if (!unit.cmd.empty())
                newUnits.push_back(std::move(unit));
        } else {
            cerr << "FATAL: failed to calculate dependence!" << endl;
            cerr << "    file:     " << file << endl;
            cerr << "    compiler: " << compiler << endl;
            cerr << "    flag:     " << ArgTable["flag"] << endl;
            cerr << "    ccflag:   " << ArgTable["ccflag"] << endl;
            cerr << "    cxxflag:  " << ArgTable["cxxflag"] << endl;
            return -1;
        }
    }

    if (hasCpp) {
        ArgTable["ld"] = ArgTable["cxx"];
    }

#ifdef DEBUG
    // Debug info.
    {
        auto fn = [](const vector<ConsUnit>& units)->void {
            for (const auto& unit: units) {
                cout << "in: " <<  unit.in << ", " << "out: " << unit.out << endl;
                cout << "\t" << unit.cmd << endl;
                cout << "\t";
                for (const auto& dep: unit.deps)
                    cout << dep << ", ";
                cout << endl;
            }
        };
        cout << "new units: " << endl;
        fn(newUnits);
    }
#endif

    // clean
    if (clean) {
        const string& cmd = "rm -f " + ArgTable["workdir"] + ArgTable["out"] + allObjects;
        cout << cmd << endl;
        ::system(cmd.c_str());
        return 0;
    }

    // compile
    if (!newUnits.empty()) {
        cout << "* Build: ";
        if (!verbose) {
            cout << newUnits.size() << (newUnits.size() > 1 ? " files" : " file");
        } else {
            for (size_t i = 0; i < newUnits.size(); ++i) {
                cout << newUnits[i].in << ((i+1 < newUnits.size()) ? ", " : "");
            }
        }
        cout << endl;

        ParallelCompiler pc(newUnits);
        pc.SetVerbose(verbose);
        if (pc.Run(std::stoi(ArgTable["jobs"])) != 0)
            return -1;
    }

    // link
    if ((!hasOut || !newUnits.empty()) && !nlink) {
        string ldCmd = ArgTable["ld"] + ArgTable["ldflag"];
        ldCmd += " -o " + ArgTable["workdir"] + ArgTable["out"] + allObjects;

        if (!verbose)
            cout << "- Link - " << ArgTable["workdir"] + ArgTable["out"] << endl;
        else
            cout << "- Link - " << ldCmd << endl;
        if (::system(ldCmd.c_str()) != 0) {
            cerr << "FATAL: failed to link!" << endl;
            cerr << "    file:   " << allObjects << endl;
            cerr << "    ld:     " << ArgTable["ld"] << endl;
            cerr << "    ldflag: " << ArgTable["ldflag"] << endl;
            return -2;
        }
    }

    return 0;
}
<|endoftext|>


<commit_before>#include <cstdlib>

#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <string>

#include <cmath>

#include "fasta_parser.h"
#include "sa_is.h"
#include "search.h"

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

  if(argc != 5) {
    std::cerr << "Usage: " << argv[0] << " <file containing reference string> <file containing query string> <index level of sparseSA, K-SA> <size of minimal match>" << std::endl;
    exit(-1);
  }

  std::ifstream ref_file(argv[1]);
	std::ifstream query_file(argv[2]);

  if(!ref_file.is_open()) {
    std::cerr << "There was a problem openning the file \"" << argv[1] << "\"!\nAborting." << std::endl;
    exit(-2);
  }

  if(!query_file.is_open()) {
    std::cerr << "There was a problem openning the file \"" << argv[2] << "\"!\nAborting." << std::endl;
    exit(-2);
  }

  // Read in reference string (fasta format)
	std::string ref_string;
	std::vector<string> refdescr;
	std::vector<long> startpos;
	fasta_parser(argv[1], ref_string, refdescr, startpos);

  // Read in query string (fasta format)
	std::string query_string;
	std::vector<string> querydescr;
	std::vector<long> q_startpos;
  fasta_parser(argv[2], query_string, querydescr, q_startpos);

  int K = atoi(argv[3]);
  int L = atoi(argv[4]);

  // pad string with $
  int pad_length = K - (ref_string.size() % K);
  ref_string.append(1, TERMINATION_CHAR);

  int N = ref_string.length();

  std::cout << ref_string << std::endl;

  std::vector<int> sa;
  sa.reserve(N);
  bool *types = new bool[N];

  int *SA = new int[N];

  int *sparseSA = new int[N / K + K];
  int *sparseISA = new int[N / K + K];
  int *sparseLCP = new int[N / K + K];

  // Creates Suffix Array using SA_IS algorithm

  sa_is(ref_string.c_str(), SA, N, 256, sizeof(char));
  ref_string.append(pad_length - 1, TERMINATION_CHAR);

  int j = 0;
  if(ref_string.size() % K != 0){
	sparseSA[j++] = (int) SA[0];
	}

  for (int i = 0; i < N; i++){
    if (SA[i] % K == 0){
      sparseSA[j++] = (int) SA[i];
    }
  }

  for (unsigned int i = 0; i < j; ++i) {
    std::cout << "[" << i << "]\t" << sparseSA[i] << (types[sparseSA[i]] ? "\tS\t" : "\tL\t")
    << ref_string.substr(sparseSA[i]) << std::endl;
  }

  cout << endl;

  // Generate ISA A
  for(int i = 0; i < j  ; i++) {
	  sparseISA[sparseSA[i] / K] = i ;
  }

  // Generate LCP
  int h = 0;
  for(int i = 0; i < j  ; i+=K) {
  int m = (int) sparseISA[i];
    if(m==0) {
      sparseLCP[m] = -1;
    }
    else {
      int j = sparseSA[m-1];
      while(i+h < N && j+h < N && ref_string[i+h] == ref_string[j+h]) {
        h++;
      }
      sparseLCP[m] = (int) h;
    }
    h = std::max(0, h - K);
  }

  cout << endl << "Sparse SA: ";
  for (int i = 0; i < j; ++i)
    cout << sparseSA[i] << "  ";

  cout << endl << "Sparse ISA: ";
  for (int i = 0; i < j; ++i)
    cout << sparseISA[i] << "  ";

  cout << endl << "Sparse LCP: ";
  for (int i = 0; i < j; ++i)
    cout << sparseLCP[i] << "  ";

  // Search for MEMs:
  cout << endl << "\tRef.\tQuery\tLength" << endl;

  int p0 = 0;
  MEM(p0, ref_string, sparseISA, sparseLCP, sparseSA, query_string, K, N, L);

  return 0;
}

<commit_msg>Expanded string<commit_after>#include <cstdlib>

#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <string>

#include <cmath>

#include "fasta_parser.h"
#include "sa_is.h"
#include "search.h"

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

  if(argc != 5) {
    std::cerr << "Usage: " << argv[0] << " <file containing reference string> <file containing query string> <index level of sparseSA, K-SA> <size of minimal match>" << std::endl;
    exit(-1);
  }

  std::ifstream ref_file(argv[1]);
	std::ifstream query_file(argv[2]);

  if(!ref_file.is_open()) {
    std::cerr << "There was a problem openning the file \"" << argv[1] << "\"!\nAborting." << std::endl;
    exit(-2);
  }

  if(!query_file.is_open()) {
    std::cerr << "There was a problem openning the file \"" << argv[2] << "\"!\nAborting." << std::endl;
    exit(-2);
  }

  // Read in reference string (fasta format)
	std::string ref_string;
	std::vector<string> refdescr;
	std::vector<long> startpos;
	fasta_parser(argv[1], ref_string, refdescr, startpos);

  // Read in query string (fasta format)
	std::string query_string;
	std::vector<string> querydescr;
	std::vector<long> q_startpos;
  fasta_parser(argv[2], query_string, querydescr, q_startpos);

  int K = atoi(argv[3]);
  int L = atoi(argv[4]);

  // pad string with $
  int pad_length = K - (ref_string.size() % K);
  ref_string.append(1, TERMINATION_CHAR);

  int N = ref_string.length();

  std::cout << ref_string << std::endl;

  std::vector<int> sa;
  sa.reserve(N);
  bool *types = new bool[N];

  int *SA = new int[N];

  int *sparseSA = new int[N / K + K];
  int *sparseISA = new int[N / K + K];
  int *sparseLCP = new int[N / K + K];

  // Creates Suffix Array using SA_IS algorithm

  sa_is(ref_string.c_str(), SA, N, 256, sizeof(char));
  ref_string.append(pad_length - 1, TERMINATION_CHAR);
  N = ref_string.size();

  int j = 0;
  if(ref_string.size() % K != 0) {
    sparseSA[j++] = (int) SA[0];
	}

  for (int i = 0; i < N; i++) {
    if (SA[i] % K == 0){
      sparseSA[j++] = (int) SA[i];
    }
  }

  for (unsigned int i = 0; i < j; ++i) {
    std::cout << "[" << i << "]\t" << sparseSA[i] << (types[sparseSA[i]] ? "\tS\t" : "\tL\t")
    << ref_string.substr(sparseSA[i]) << std::endl;
  }

  cout << endl;

  // Generate ISA A
  for(int i = 0; i < j  ; i++) {
	  sparseISA[sparseSA[i] / K] = i ;
  }

  // Generate LCP
  int h = 0;
  for(int i = 0; i < j  ; i+=K) {
  int m = (int) sparseISA[i];
    if(m==0) {
      sparseLCP[m] = -1;
    }
    else {
      int j = sparseSA[m-1];
      while(i+h < N && j+h < N && ref_string[i+h] == ref_string[j+h]) {
        h++;
      }
      sparseLCP[m] = (int) h;
    }
    h = std::max(0, h - K);
  }

  cout << endl << "Sparse SA: ";
  for (int i = 0; i < j; ++i)
    cout << sparseSA[i] << "  ";

  cout << endl << "Sparse ISA: ";
  for (int i = 0; i < j; ++i)
    cout << sparseISA[i] << "  ";

  cout << endl << "Sparse LCP: ";
  for (int i = 0; i < j; ++i)
    cout << sparseLCP[i] << "  ";

  // Search for MEMs:
  cout << endl << "\tRef.\tQuery\tLength" << endl;

  int p0 = 0;
  MEM(p0, ref_string, sparseISA, sparseLCP, sparseSA, query_string, K, N, L);

  return 0;
}

<|endoftext|>


<commit_before>#include <unistd.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <time.h>
#include <string.h>
using namespace std;

#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Output.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Value_Slider.H>
#include <FL/Fl_File_Chooser.H>
#include "Fl_Rotated_Text/Fl_Rotated_Text.H"
#include "cartesian/Cartesian.H"

#include "cvFltkWidget.hh"
#include "ffmpegInterface.hh"
#include "cameraSource.hh"

extern "C"
{
#include "wormProcessing.h"
}

#define DATA_FRAME_RATE_FPS     (1.0 / 15.0) /* I collect at 15 seconds per frame */
#define SETUP_FRAME_RATE_FPS    5
#define CIRCLE_RADIUS           50
#define CIRCLE_COLOR            CV_RGB(0xFF, 0, 0)
#define POINTED_CIRCLE_COLOR    CV_RGB(0, 0xFF, 0)

#define FRAME_W       480
#define FRAME_H       480
#define CROP_RECT     cvRect(80, 0, FRAME_W, FRAME_H)
#define WINDOW_W      1000
#define WINDOW_H      (FRAME_H + BUTTON_H + PLOT_H + X_AXIS_HEIGHT + AXIS_EXTRA_SPACE)
#define BUTTON_W      100
#define BUTTON_H      30
#define PLOT_W        (WINDOW_W - (Y_AXIS_WIDTH + AXIS_EXTRA_SPACE))
#define PLOT_H        400
#define X_AXIS_HEIGHT 30
#define Y_AXIS_WIDTH  80
#define ACCUM_W       100
#define ACCUM_H       BUTTON_H

// due to a bug (most likely), the axis aren't drawn completely inside their box. Thus I leave a bit
// of extra space to see the labels
#define AXIS_EXTRA_SPACE 40

#define AM_READING_CAMERA (dynamic_cast<CameraSource*>(source) != NULL)

static FrameSource*  source;
static CvFltkWidget* widgetImage;
static Fl_Button*    goResetButton;
static Ca_Canvas*    plot = NULL;
static Ca_X_Axis*    Xaxis;
static Ca_Y_Axis*    Yaxis;

static Fl_Output* leftAccum;
static Fl_Output* rightAccum;
static double     leftAccumValue  = 0.0;
static double     rightAccumValue = 0.0;

// the analysis could be idle, running, or idle examining data (STOPPED)
static enum { RESET, RUNNING, STOPPED } analysisState;

static int           numPoints;
static Ca_LinePoint* lastLeftPoint       = NULL;
static Ca_LinePoint* lastRightPoint      = NULL;
static CvPoint       leftCircleCenter    = cvPoint(-1, -1);
static CvPoint       rightCircleCenter   = cvPoint(-1, -1);
static CvPoint       pointedCircleCenter = cvPoint(-1, -1);

#define HAVE_LEFT_CIRCLE    (leftCircleCenter .x > 0 && leftCircleCenter .y > 0)
#define HAVE_RIGHT_CIRCLE   (rightCircleCenter.x > 0 && rightCircleCenter.y > 0)
#define HAVE_CIRCLES        (HAVE_LEFT_CIRCLE && HAVE_RIGHT_CIRCLE)
#define HAVE_POINTED_CIRCLE (pointedCircleCenter.x > 0 && pointedCircleCenter.y > 0)

void gotNewFrame(IplImage* buffer, uint64_t timestamp_us __attribute__((unused)))
{
    cvMerge(buffer, buffer, buffer, NULL, *widgetImage);

    const CvMat* result = isolateWorms(buffer);
    cvSetImageCOI(*widgetImage, 1);
    cvCopy(result, *widgetImage);
    cvSetImageCOI(*widgetImage, 0);

    if(HAVE_LEFT_CIRCLE)
        cvCircle(*widgetImage, leftCircleCenter,    CIRCLE_RADIUS, CIRCLE_COLOR, 1, 8);

    if(HAVE_RIGHT_CIRCLE)
        cvCircle(*widgetImage, rightCircleCenter,   CIRCLE_RADIUS, CIRCLE_COLOR, 1, 8);

    if(HAVE_POINTED_CIRCLE)
        cvCircle(*widgetImage, pointedCircleCenter, CIRCLE_RADIUS, POINTED_CIRCLE_COLOR, 1, 8);

    // This critical section is likely larger than it needs to be, but this keeps me safe. The
    // analysis state can change in the FLTK thread, so I err on the side of safety
    Fl::lock();
    {
        if(analysisState == RUNNING)
        {
            double minutes = (double)numPoints / DATA_FRAME_RATE_FPS / 60.0;
            double leftOccupancy, rightOccupancy;
            computeWormOccupancy(result, &leftCircleCenter, &rightCircleCenter,
                                 CIRCLE_RADIUS,
                                 &leftOccupancy, &rightOccupancy);

            Yaxis->rescale(CA_WHEN_MAX, fmax(leftOccupancy, rightOccupancy) );

            lastLeftPoint  = new Ca_LinePoint(lastLeftPoint,
                                              minutes,
                                              leftOccupancy,  1,FL_RED,   CA_NO_POINT);
            lastRightPoint = new Ca_LinePoint(lastRightPoint,
                                              minutes,
                                              rightOccupancy, 1,FL_GREEN, CA_NO_POINT);
            Xaxis->maximum(minutes);
            numPoints++;

            leftAccumValue  += leftOccupancy  / DATA_FRAME_RATE_FPS;
            rightAccumValue += rightOccupancy / DATA_FRAME_RATE_FPS;
            char results[128];
            snprintf(results, sizeof(results), "%.3f", leftAccumValue);
            leftAccum->value(results);
            snprintf(results, sizeof(results), "%.3f", rightAccumValue);
            rightAccum->value(results);
        }

        widgetImage->redrawNewFrame();
    }

    if(!AM_READING_CAMERA && analysisState != RUNNING)
    {
        Fl::unlock();

        // reading from a video file and not actually running the analysis yet. In this case I
        // rewind back to the beginning and delay, to force a reasonable refresh rate
        source->restartStream();

        struct timespec tv;
        tv.tv_sec  = 0;
        tv.tv_nsec = 1e9 / SETUP_FRAME_RATE_FPS;
        nanosleep(&tv, NULL);
    }
    else
        Fl::unlock();
}

static void widgetImageCallback(Fl_Widget* widget __attribute__((unused)), void* cookie __attribute__((unused)))
{
    if(Fl::event() == FL_LEAVE)
    {
        // I want to make sure to never miss this, so I handle it unconditionally
        pointedCircleCenter.x = pointedCircleCenter.y = -1;
        return;
    }

    if(analysisState == RUNNING)
    {
        pointedCircleCenter.x = pointedCircleCenter.y = -1;
        return;
    }

    bool onLeftHalf = (Fl::event_x() - widget->x()) < FRAME_W/2;
    switch(Fl::event())
    {
    case FL_MOVE:
        pointedCircleCenter.x = Fl::event_x() - widget->x();
        pointedCircleCenter.y = Fl::event_y() - widget->y();
        break;

    case FL_LEAVE:
        pointedCircleCenter.x = pointedCircleCenter.y = -1;
        break;

    case FL_PUSH:
        if(onLeftHalf)
        {
            leftCircleCenter.x = Fl::event_x() - widget->x();
            leftCircleCenter.y = Fl::event_y() - widget->y();
        }
        else
        {
            rightCircleCenter.x = Fl::event_x() - widget->x();
            rightCircleCenter.y = Fl::event_y() - widget->y();
        }

        pointedCircleCenter.x = pointedCircleCenter.y = -1;
        break;

    default: ;
    }

    if(HAVE_CIRCLES && analysisState == RESET)
        goResetButton->activate();
}

static void setResetAnalysis(void)
{
    goResetButton->labelfont(FL_HELVETICA_BOLD);
    goResetButton->labelcolor(FL_RED);
    goResetButton->type(FL_TOGGLE_BUTTON);
    goResetButton->label("Analyze");

    numPoints       = 0;
    if(plot) plot->clear();
    lastLeftPoint   = lastRightPoint = NULL;
    leftAccumValue  = 0.0;
    rightAccumValue = 0.0;
    leftAccum ->value("0.0");
    rightAccum->value("0.0");

    if(!AM_READING_CAMERA)
        source->restartStream();

    analysisState = RESET;
}

static void setRunningAnalysis(void)
{
    goResetButton->labelfont(FL_HELVETICA);
    goResetButton->labelcolor(FL_BLACK);
    goResetButton->type(FL_TOGGLE_BUTTON);
    goResetButton->label("Stop analysis");

    pointedCircleCenter.x = pointedCircleCenter.y = -1;

    analysisState = RUNNING;
}

static void setStoppedAnalysis(void)
{
    goResetButton->labelfont(FL_HELVETICA);
    goResetButton->labelcolor(FL_BLACK);
    goResetButton->type(FL_NORMAL_BUTTON);
    goResetButton->label("Reset analysis data");

    analysisState = STOPPED;
}

static void pressedGoReset(Fl_Widget* widget __attribute__((unused)), void* cookie __attribute__((unused)))
{
    if     (analysisState == RUNNING) setStoppedAnalysis();
    else if(analysisState == STOPPED) setResetAnalysis();
    else                              setRunningAnalysis();
}

int main(int argc, char* argv[])
{
    Fl::lock();
    Fl::visual(FL_RGB);

    // open the first source. If there's an argument, assume it's an input video. Otherwise, try
    // reading a camera
    if(argc >= 2) source = new FFmpegDecoder(argv[1], FRAMESOURCE_GRAYSCALE, false, CROP_RECT);
    else          source = new CameraSource (FRAMESOURCE_GRAYSCALE, false, 0, CROP_RECT);

    if(! *source)
    {
        fprintf(stderr, "couldn't open source\n");
        delete source;
        return 0;
    }

    Fl_Double_Window* window =
        new Fl_Double_Window(WINDOW_W, WINDOW_H, "Wormtracker 3");
    widgetImage = new CvFltkWidget(0, 0, source->w(), source->h(),
                                   WIDGET_COLOR);

    widgetImage->callback(widgetImageCallback);

    goResetButton = new Fl_Button( 0, source->h(), BUTTON_W, BUTTON_H);
    goResetButton->callback(pressedGoReset);
    goResetButton->deactivate();

    plot = new Ca_Canvas( Y_AXIS_WIDTH + AXIS_EXTRA_SPACE, goResetButton->y() + goResetButton->h(), PLOT_W, PLOT_H,
                          "Worm occupancy");
    plot->align(FL_ALIGN_TOP);

    // This is extremely important for some reason. Without it the plots do not refresh property and
    // there're artifacts every time the plot is resized
    plot->box(FL_DOWN_BOX);

    Xaxis = new Ca_X_Axis(plot->x(), plot->y() + plot->h(), plot->w(), X_AXIS_HEIGHT, "Minutes");
    Xaxis->align(FL_ALIGN_BOTTOM);
    Xaxis->minimum(0);
    Xaxis->maximum(1);
    Xaxis->label_format("%g");
    Xaxis->major_step(10);
    Xaxis->label_step(10);
    Xaxis->axis_color(FL_BLACK);
    Xaxis->axis_align(CA_BOTTOM | CA_LINE);

    Yaxis = new Ca_Y_Axis(AXIS_EXTRA_SPACE, plot->y(), Y_AXIS_WIDTH, plot->h());
    Fl_Rotated_Text YaxisLabel("occupancy ratio", FL_HELVETICA, 14, 0, 1);
    Yaxis->image(&YaxisLabel);
    Yaxis->minimum(0);
    Yaxis->maximum(0.01);
    Yaxis->align(FL_ALIGN_LEFT);
    Yaxis->axis_align(CA_LEFT | CA_LINE);
    Yaxis->axis_color(FL_BLACK);

    leftAccum  = new Fl_Output(widgetImage->x() + widgetImage->w(), widgetImage->y(),
                              ACCUM_W, ACCUM_H, "Left accumulator (occupancy-seconds)");
    rightAccum = new Fl_Output(leftAccum->x(), leftAccum->y() + leftAccum->h(),
                              ACCUM_W, ACCUM_H, "Right accumulator (occupancy-seconds)");
    leftAccum ->align(FL_ALIGN_RIGHT);
    rightAccum->align(FL_ALIGN_RIGHT);
    leftAccum ->labelcolor(FL_RED);
    rightAccum->labelcolor(FL_GREEN);

    window->end();
    window->show();

    processingInit(source->w(), source->h());

    setResetAnalysis();

    // I read the data with a tiny delay. This makes sure that I skip old frames (only an issue if I
    // can't keep up with the data rate), but yet got as fast as I can
    IplImage* buffer = cvCreateImage(cvSize(source->w(), source->h()), IPL_DEPTH_8U, 1);

    // If reading from a stored video file, go as fast as possible
    if(AM_READING_CAMERA) source->startSourceThread(&gotNewFrame, 1e6/DATA_FRAME_RATE_FPS, buffer);
    else                  source->startSourceThread(&gotNewFrame, 0,                       buffer);

    while (Fl::wait())
    {
    }

    Fl::unlock();

    delete source;
    delete window;
    cvReleaseImage(&buffer);

    processingCleanup();
    return 0;
}
<commit_msg>updated my capture thread to match the new API<commit_after>#include <unistd.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <time.h>
#include <string.h>
using namespace std;

#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Output.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Value_Slider.H>
#include <FL/Fl_File_Chooser.H>
#include "Fl_Rotated_Text/Fl_Rotated_Text.H"
#include "cartesian/Cartesian.H"

#include "cvFltkWidget.hh"
#include "ffmpegInterface.hh"
#include "cameraSource.hh"

extern "C"
{
#include "wormProcessing.h"
}

#define DATA_FRAME_RATE_FPS     (1.0 / 15.0) /* I collect at 15 seconds per frame */
#define SETUP_FRAME_RATE_FPS    5
#define CIRCLE_RADIUS           50
#define CIRCLE_COLOR            CV_RGB(0xFF, 0, 0)
#define POINTED_CIRCLE_COLOR    CV_RGB(0, 0xFF, 0)

#define FRAME_W       480
#define FRAME_H       480
#define CROP_RECT     cvRect(80, 0, FRAME_W, FRAME_H)
#define WINDOW_W      1000
#define WINDOW_H      (FRAME_H + BUTTON_H + PLOT_H + X_AXIS_HEIGHT + AXIS_EXTRA_SPACE)
#define BUTTON_W      100
#define BUTTON_H      30
#define PLOT_W        (WINDOW_W - (Y_AXIS_WIDTH + AXIS_EXTRA_SPACE))
#define PLOT_H        400
#define X_AXIS_HEIGHT 30
#define Y_AXIS_WIDTH  80
#define ACCUM_W       100
#define ACCUM_H       BUTTON_H

// due to a bug (most likely), the axis aren't drawn completely inside their box. Thus I leave a bit
// of extra space to see the labels
#define AXIS_EXTRA_SPACE 40

#define AM_READING_CAMERA (dynamic_cast<CameraSource*>(source) != NULL)

static FrameSource*  source;
static CvFltkWidget* widgetImage;
static Fl_Button*    goResetButton;
static Ca_Canvas*    plot = NULL;
static Ca_X_Axis*    Xaxis;
static Ca_Y_Axis*    Yaxis;

static Fl_Output* leftAccum;
static Fl_Output* rightAccum;
static double     leftAccumValue  = 0.0;
static double     rightAccumValue = 0.0;

// the analysis could be idle, running, or idle examining data (STOPPED)
static enum { RESET, RUNNING, STOPPED } analysisState;

static int           numPoints;
static Ca_LinePoint* lastLeftPoint       = NULL;
static Ca_LinePoint* lastRightPoint      = NULL;
static CvPoint       leftCircleCenter    = cvPoint(-1, -1);
static CvPoint       rightCircleCenter   = cvPoint(-1, -1);
static CvPoint       pointedCircleCenter = cvPoint(-1, -1);

#define HAVE_LEFT_CIRCLE    (leftCircleCenter .x > 0 && leftCircleCenter .y > 0)
#define HAVE_RIGHT_CIRCLE   (rightCircleCenter.x > 0 && rightCircleCenter.y > 0)
#define HAVE_CIRCLES        (HAVE_LEFT_CIRCLE && HAVE_RIGHT_CIRCLE)
#define HAVE_POINTED_CIRCLE (pointedCircleCenter.x > 0 && pointedCircleCenter.y > 0)

bool gotNewFrame(IplImage* buffer, uint64_t timestamp_us __attribute__((unused)))
{
    cvMerge(buffer, buffer, buffer, NULL, *widgetImage);

    const CvMat* result = isolateWorms(buffer);
    cvSetImageCOI(*widgetImage, 1);
    cvCopy(result, *widgetImage);
    cvSetImageCOI(*widgetImage, 0);

    if(HAVE_LEFT_CIRCLE)
        cvCircle(*widgetImage, leftCircleCenter,    CIRCLE_RADIUS, CIRCLE_COLOR, 1, 8);

    if(HAVE_RIGHT_CIRCLE)
        cvCircle(*widgetImage, rightCircleCenter,   CIRCLE_RADIUS, CIRCLE_COLOR, 1, 8);

    if(HAVE_POINTED_CIRCLE)
        cvCircle(*widgetImage, pointedCircleCenter, CIRCLE_RADIUS, POINTED_CIRCLE_COLOR, 1, 8);

    // This critical section is likely larger than it needs to be, but this keeps me safe. The
    // analysis state can change in the FLTK thread, so I err on the side of safety
    Fl::lock();
    {
        if(analysisState == RUNNING)
        {
            double minutes = (double)numPoints / DATA_FRAME_RATE_FPS / 60.0;
            double leftOccupancy, rightOccupancy;
            computeWormOccupancy(result, &leftCircleCenter, &rightCircleCenter,
                                 CIRCLE_RADIUS,
                                 &leftOccupancy, &rightOccupancy);

            Yaxis->rescale(CA_WHEN_MAX, fmax(leftOccupancy, rightOccupancy) );

            lastLeftPoint  = new Ca_LinePoint(lastLeftPoint,
                                              minutes,
                                              leftOccupancy,  1,FL_RED,   CA_NO_POINT);
            lastRightPoint = new Ca_LinePoint(lastRightPoint,
                                              minutes,
                                              rightOccupancy, 1,FL_GREEN, CA_NO_POINT);
            Xaxis->maximum(minutes);
            numPoints++;

            leftAccumValue  += leftOccupancy  / DATA_FRAME_RATE_FPS;
            rightAccumValue += rightOccupancy / DATA_FRAME_RATE_FPS;
            char results[128];
            snprintf(results, sizeof(results), "%.3f", leftAccumValue);
            leftAccum->value(results);
            snprintf(results, sizeof(results), "%.3f", rightAccumValue);
            rightAccum->value(results);
        }

        widgetImage->redrawNewFrame();
    }

    if(!AM_READING_CAMERA && analysisState != RUNNING)
    {
        Fl::unlock();

        // reading from a video file and not actually running the analysis yet. In this case I
        // rewind back to the beginning and delay, to force a reasonable refresh rate
        source->restartStream();

        struct timespec tv;
        tv.tv_sec  = 0;
        tv.tv_nsec = 1e9 / SETUP_FRAME_RATE_FPS;
        nanosleep(&tv, NULL);
    }
    else
        Fl::unlock();
}

static void widgetImageCallback(Fl_Widget* widget __attribute__((unused)), void* cookie __attribute__((unused)))
{
    if(Fl::event() == FL_LEAVE)
    {
        // I want to make sure to never miss this, so I handle it unconditionally
        pointedCircleCenter.x = pointedCircleCenter.y = -1;
        return;
    }

    if(analysisState == RUNNING)
    {
        pointedCircleCenter.x = pointedCircleCenter.y = -1;
        return;
    }

    bool onLeftHalf = (Fl::event_x() - widget->x()) < FRAME_W/2;
    switch(Fl::event())
    {
    case FL_MOVE:
        pointedCircleCenter.x = Fl::event_x() - widget->x();
        pointedCircleCenter.y = Fl::event_y() - widget->y();
        break;

    case FL_LEAVE:
        pointedCircleCenter.x = pointedCircleCenter.y = -1;
        break;

    case FL_PUSH:
        if(onLeftHalf)
        {
            leftCircleCenter.x = Fl::event_x() - widget->x();
            leftCircleCenter.y = Fl::event_y() - widget->y();
        }
        else
        {
            rightCircleCenter.x = Fl::event_x() - widget->x();
            rightCircleCenter.y = Fl::event_y() - widget->y();
        }

        pointedCircleCenter.x = pointedCircleCenter.y = -1;
        break;

    default: ;
    }

    if(HAVE_CIRCLES && analysisState == RESET)
        goResetButton->activate();
}

static void setResetAnalysis(void)
{
    goResetButton->labelfont(FL_HELVETICA_BOLD);
    goResetButton->labelcolor(FL_RED);
    goResetButton->type(FL_TOGGLE_BUTTON);
    goResetButton->label("Analyze");

    numPoints       = 0;
    if(plot) plot->clear();
    lastLeftPoint   = lastRightPoint = NULL;
    leftAccumValue  = 0.0;
    rightAccumValue = 0.0;
    leftAccum ->value("0.0");
    rightAccum->value("0.0");

    if(!AM_READING_CAMERA)
        source->restartStream();

    analysisState = RESET;
}

static void setRunningAnalysis(void)
{
    goResetButton->labelfont(FL_HELVETICA);
    goResetButton->labelcolor(FL_BLACK);
    goResetButton->type(FL_TOGGLE_BUTTON);
    goResetButton->label("Stop analysis");

    pointedCircleCenter.x = pointedCircleCenter.y = -1;

    analysisState = RUNNING;
}

static void setStoppedAnalysis(void)
{
    goResetButton->labelfont(FL_HELVETICA);
    goResetButton->labelcolor(FL_BLACK);
    goResetButton->type(FL_NORMAL_BUTTON);
    goResetButton->label("Reset analysis data");

    analysisState = STOPPED;
}

static void pressedGoReset(Fl_Widget* widget __attribute__((unused)), void* cookie __attribute__((unused)))
{
    if     (analysisState == RUNNING) setStoppedAnalysis();
    else if(analysisState == STOPPED) setResetAnalysis();
    else                              setRunningAnalysis();
}

int main(int argc, char* argv[])
{
    Fl::lock();
    Fl::visual(FL_RGB);

    // open the first source. If there's an argument, assume it's an input video. Otherwise, try
    // reading a camera
    if(argc >= 2) source = new FFmpegDecoder(argv[1], FRAMESOURCE_GRAYSCALE, false, CROP_RECT);
    else          source = new CameraSource (FRAMESOURCE_GRAYSCALE, false, 0, CROP_RECT);

    if(! *source)
    {
        fprintf(stderr, "couldn't open source\n");
        delete source;
        return 0;
    }

    Fl_Double_Window* window =
        new Fl_Double_Window(WINDOW_W, WINDOW_H, "Wormtracker 3");
    widgetImage = new CvFltkWidget(0, 0, source->w(), source->h(),
                                   WIDGET_COLOR);

    widgetImage->callback(widgetImageCallback);

    goResetButton = new Fl_Button( 0, source->h(), BUTTON_W, BUTTON_H);
    goResetButton->callback(pressedGoReset);
    goResetButton->deactivate();

    plot = new Ca_Canvas( Y_AXIS_WIDTH + AXIS_EXTRA_SPACE, goResetButton->y() + goResetButton->h(), PLOT_W, PLOT_H,
                          "Worm occupancy");
    plot->align(FL_ALIGN_TOP);

    // This is extremely important for some reason. Without it the plots do not refresh property and
    // there're artifacts every time the plot is resized
    plot->box(FL_DOWN_BOX);

    Xaxis = new Ca_X_Axis(plot->x(), plot->y() + plot->h(), plot->w(), X_AXIS_HEIGHT, "Minutes");
    Xaxis->align(FL_ALIGN_BOTTOM);
    Xaxis->minimum(0);
    Xaxis->maximum(1);
    Xaxis->label_format("%g");
    Xaxis->major_step(10);
    Xaxis->label_step(10);
    Xaxis->axis_color(FL_BLACK);
    Xaxis->axis_align(CA_BOTTOM | CA_LINE);

    Yaxis = new Ca_Y_Axis(AXIS_EXTRA_SPACE, plot->y(), Y_AXIS_WIDTH, plot->h());
    Fl_Rotated_Text YaxisLabel("occupancy ratio", FL_HELVETICA, 14, 0, 1);
    Yaxis->image(&YaxisLabel);
    Yaxis->minimum(0);
    Yaxis->maximum(0.01);
    Yaxis->align(FL_ALIGN_LEFT);
    Yaxis->axis_align(CA_LEFT | CA_LINE);
    Yaxis->axis_color(FL_BLACK);

    leftAccum  = new Fl_Output(widgetImage->x() + widgetImage->w(), widgetImage->y(),
                              ACCUM_W, ACCUM_H, "Left accumulator (occupancy-seconds)");
    rightAccum = new Fl_Output(leftAccum->x(), leftAccum->y() + leftAccum->h(),
                              ACCUM_W, ACCUM_H, "Right accumulator (occupancy-seconds)");
    leftAccum ->align(FL_ALIGN_RIGHT);
    rightAccum->align(FL_ALIGN_RIGHT);
    leftAccum ->labelcolor(FL_RED);
    rightAccum->labelcolor(FL_GREEN);

    window->end();
    window->show();

    processingInit(source->w(), source->h());

    setResetAnalysis();

    // I read the data with a tiny delay. This makes sure that I skip old frames (only an issue if I
    // can't keep up with the data rate), but yet got as fast as I can
    IplImage* buffer = cvCreateImage(cvSize(source->w(), source->h()), IPL_DEPTH_8U, 1);

    // If reading from a stored video file, go as fast as possible
    if(AM_READING_CAMERA) source->startSourceThread(&gotNewFrame, 1e6/DATA_FRAME_RATE_FPS, buffer);
    else                  source->startSourceThread(&gotNewFrame, 0,                       buffer);

    while (Fl::wait())
    {
    }

    Fl::unlock();

    delete source;
    delete window;
    cvReleaseImage(&buffer);

    processingCleanup();
    return 0;
}
<|endoftext|>