<commit_before>
#include "diff_system.h"
#include "libmesh_logging.h"
#include "linear_solver.h"
#include "newton_solver.h"
#include "numeric_vector.h"
#include "sparse_matrix.h"
#include "dof_map.h"

NewtonSolver::NewtonSolver (sys_type& s)
  : Parent(s),
    require_residual_reduction(false),
    minsteplength(0.0),
    linear_solver(LinearSolver<Number>::build())
{
}

NewtonSolver::~NewtonSolver ()
{
}

void NewtonSolver::reinit()
{
  Parent::reinit();

  linear_solver->clear();
}

void NewtonSolver::solve()
{
  START_LOG("solve()", "NewtonSolver");

// Amount by which nonlinear residual should exceed linear solver
// tolerance
const Real relative_tolerance = 1.e-3;

  NumericVector<Number> &solution = *(_system.solution);
  NumericVector<Number> &newton_iterate =
    _system.get_vector("_nonlinear_solution");
  newton_iterate.close();
  solution.close();

  NumericVector<Number> &rhs = *(_system.rhs);

  SparseMatrix<Number> &matrix = *(_system.matrix);

  // Prepare to take incomplete steps
  Real last_residual=0.;

  // Set starting linear tolerance
  Real current_linear_tolerance = initial_linear_tolerance;

  // Now we begin the nonlinear loop
  for (unsigned int l=0; l<max_nonlinear_iterations; ++l)
    {
      if (!quiet)
        std::cout << "Assembling System" << std::endl;

      PAUSE_LOG("solve()", "NewtonSolver");
      _system.assembly(true, true);
      RESTART_LOG("solve()", "NewtonSolver");
      rhs.close();
      Real current_residual = rhs.l2_norm();
      last_residual = current_residual;

      max_residual_norm = std::max (current_residual,
                                    max_residual_norm);

      // Compute the l2 norm of the whole solution
      Real norm_total = newton_iterate.l2_norm();

      max_solution_norm = std::max(max_solution_norm, norm_total);

      if (!quiet)
        std::cout << "Nonlinear Residual: "
                  << current_residual << std::endl;

      // Make sure our linear tolerance is low enough
      if (current_linear_tolerance > current_residual * relative_tolerance)
        {
          current_linear_tolerance = current_residual * relative_tolerance;
        }

      // But don't let it be zero
      if (current_linear_tolerance == 0.)
        {
          current_linear_tolerance = TOLERANCE * TOLERANCE;
        }

      // At this point newton_iterate is the current guess, and
      // solution is now about to become the NEGATIVE of the next
      // Newton step.

      // Our best initial guess for the solution is zero!
      solution.zero();

      if (!quiet)
        std::cout << "Linear solve starting" << std::endl;

      PAUSE_LOG("solve()", "NewtonSolver");
      // Solve the linear system.  Two cases:
      const std::pair<unsigned int, Real> rval =
        (_system.have_matrix("Preconditioner")) ?
      // 1.) User-supplied preconditioner
        linear_solver->solve (matrix, _system.get_matrix("Preconditioner"),
                              solution, rhs, current_linear_tolerance,
                              max_linear_iterations) :
      // 2.) Use system matrix for the preconditioner
        linear_solver->solve (matrix, solution, rhs,
                              current_linear_tolerance,
                              max_linear_iterations);
      // We may need to localize a parallel solution
      _system.update ();
      RESTART_LOG("solve()", "NewtonSolver");
      // The linear solver may not have fit our constraints exactly
      _system.get_dof_map().enforce_constraints_exactly(_system);

      if (!quiet)
        std::cout << "Linear solve finished, step " << rval.first
                  << ", residual " << rval.second
                  << ", tolerance " << current_linear_tolerance
                  << std::endl;

      // Compute the l2 norm of the nonlinear update
      Real norm_delta = solution.l2_norm();

      if (!quiet)
        std::cout << "Trying full Newton step" << std::endl;
      // Take a full Newton step
      newton_iterate.add (-1., solution);
      newton_iterate.close();

      // Check residual with full Newton step
      Real steplength = 1.;
      PAUSE_LOG("solve()", "NewtonSolver");
      _system.assembly(true, false);
      RESTART_LOG("solve()", "NewtonSolver");

      rhs.close();
      current_residual = rhs.l2_norm();

      // backtrack if necessary
      if (require_residual_reduction)
        {
          // but don't fiddle around if we've already converged
          if (test_convergence(current_residual, norm_delta))
            {
              if (!quiet)
		print_convergence(l, current_residual, norm_delta);
              break;
            }

          while (current_residual > last_residual)
            {
              // Reduce step size to 1/2, 1/4, etc.
              steplength /= 2.;
              norm_delta /= 2.;
              if (!quiet)
                std::cout << "Shrinking Newton step to "
                          << steplength << std::endl;
              newton_iterate.add (steplength, solution);
              newton_iterate.close();

              // Check residual with fractional Newton step
              PAUSE_LOG("solve()", "NewtonSolver");
              _system.assembly (true, false);
              RESTART_LOG("solve()", "NewtonSolver");
              current_residual = rhs.l2_norm();
              if (!quiet)
                std::cout << "Current Residual: "
                          << current_residual << std::endl;

              if (steplength/2. < minsteplength &&
                  current_residual > last_residual)
                {
                  if (!quiet)
                    std::cout << "Inexact Newton step FAILED at step "
                              << l << std::endl;

                  error();
                }
            }
        }

      // Compute the l2 norm of the whole solution
      norm_total = newton_iterate.l2_norm();

      max_solution_norm = std::max(max_solution_norm, norm_total);

      // Print out information for the
      // nonlinear iterations.
      if (!quiet)
        std::cout << "  Nonlinear step: |du|/|u| = "
                  << norm_delta / norm_total
                  << ", |du| = " << norm_delta
                  << std::endl;

      // Terminate the solution iteration if the difference between
      // this iteration and the last is sufficiently small.
      if (!quiet)
        print_convergence(l, current_residual,
                          norm_delta / steplength);
      if (test_convergence(current_residual, norm_delta / steplength))
        {
          break;
        }
      if (l >= max_nonlinear_iterations - 1)
        {
          std::cout << "  Nonlinear solver DIVERGED at step " << l
                    << " with norm " << norm_total
                    << std::endl;
          error();
          continue;
        }
    } // end nonlinear loop

  // Copy the final nonlinear iterate into the current_solution,
  // for other libMesh functions that expect it

  solution = newton_iterate;
  solution.close();

  // We may need to localize a parallel solution
  _system.update ();

  STOP_LOG("solve()", "NewtonSolver");
}

bool NewtonSolver::test_convergence(Real current_residual,
                                    Real step_norm)
{
  // We haven't converged unless we pass a convergence test
  bool has_converged = false;

  // Is our absolute residual low enough?
  if (current_residual < absolute_residual_tolerance)
    has_converged = true;

  // Is our relative residual low enough?
  if ((current_residual / max_residual_norm) <
      relative_residual_tolerance)
    has_converged = true;

  // Is our absolute Newton step size small enough?
  if (step_norm < absolute_step_tolerance)
    has_converged = true;

  // Is our relative Newton step size small enough?
  if (step_norm / max_solution_norm <
      relative_step_tolerance)
    has_converged = true;

  return has_converged;
}

void NewtonSolver::print_convergence(unsigned int step_num,
                                     Real current_residual,
                                     Real step_norm)
{
  // Is our absolute residual low enough?
  if (current_residual < absolute_residual_tolerance)
    {
      std::cout << "  Nonlinear solver converged, step " << step_num
                << ", residual " << current_residual
                << std::endl;
    }
  else if (absolute_residual_tolerance)
    {
      std::cout << "  Nonlinear solver current_residual "
                << current_residual << " > "
                << (absolute_residual_tolerance) << std::endl;
    }

  // Is our relative residual low enough?
  if ((current_residual / max_residual_norm) <
      relative_residual_tolerance)
    {
      std::cout << "  Nonlinear solver converged, step " << step_num
                << ", residual reduction "
                << current_residual / max_residual_norm
                << " < " << relative_residual_tolerance
                << std::endl;
    }
  else if (relative_residual_tolerance)
    {
      if (!quiet)
        std::cout << "  Nonlinear solver relative residual "
                  << (current_residual / max_residual_norm)
                  << " > " << relative_residual_tolerance
                  << std::endl;
    }

  // Is our absolute Newton step size small enough?
  if (step_norm < absolute_step_tolerance)
    {
      std::cout << "  Nonlinear solver converged, step " << step_num
                << ", absolute step size "
                << step_norm
                << " < " << absolute_step_tolerance
                << std::endl;
    }
  else if (absolute_step_tolerance)
    {
      std::cout << "  Nonlinear solver absolute step size "
                << step_norm
                << " > " << absolute_step_tolerance
                << std::endl;
    }

  // Is our relative Newton step size small enough?
  if (step_norm / max_solution_norm <
      relative_step_tolerance)
    {
      std::cout << "  Nonlinear solver converged, step " << step_num
                << ", relative step size "
                << (step_norm / max_solution_norm)
                << " < " << relative_step_tolerance
                << std::endl;
    }
  else if (relative_step_tolerance)
    {
      std::cout << "  Nonlinear solver relative step size "
                << (step_norm / max_solution_norm)
                << " > " << relative_step_tolerance
                << std::endl;
    }
}
<commit_msg>Avoid redundant close() calls<commit_after>
#include "diff_system.h"
#include "libmesh_logging.h"
#include "linear_solver.h"
#include "newton_solver.h"
#include "numeric_vector.h"
#include "sparse_matrix.h"
#include "dof_map.h"

NewtonSolver::NewtonSolver (sys_type& s)
  : Parent(s),
    require_residual_reduction(false),
    minsteplength(0.0),
    linear_solver(LinearSolver<Number>::build())
{
}

NewtonSolver::~NewtonSolver ()
{
}

void NewtonSolver::reinit()
{
  Parent::reinit();

  linear_solver->clear();
}

void NewtonSolver::solve()
{
  START_LOG("solve()", "NewtonSolver");

// Amount by which nonlinear residual should exceed linear solver
// tolerance
const Real relative_tolerance = 1.e-3;

  NumericVector<Number> &solution = *(_system.solution);
  NumericVector<Number> &newton_iterate =
    _system.get_vector("_nonlinear_solution");
  newton_iterate.close();
//  solution.close();

  NumericVector<Number> &rhs = *(_system.rhs);

  SparseMatrix<Number> &matrix = *(_system.matrix);

  // Prepare to take incomplete steps
  Real last_residual=0.;

  // Set starting linear tolerance
  Real current_linear_tolerance = initial_linear_tolerance;

  // Now we begin the nonlinear loop
  for (unsigned int l=0; l<max_nonlinear_iterations; ++l)
    {
      if (!quiet)
        std::cout << "Assembling System" << std::endl;

      PAUSE_LOG("solve()", "NewtonSolver");
      _system.assembly(true, true);
      RESTART_LOG("solve()", "NewtonSolver");
      rhs.close();
      Real current_residual = rhs.l2_norm();
      last_residual = current_residual;

      max_residual_norm = std::max (current_residual,
                                    max_residual_norm);

      // Compute the l2 norm of the whole solution
      Real norm_total = newton_iterate.l2_norm();

      max_solution_norm = std::max(max_solution_norm, norm_total);

      if (!quiet)
        std::cout << "Nonlinear Residual: "
                  << current_residual << std::endl;

      // Make sure our linear tolerance is low enough
      if (current_linear_tolerance > current_residual * relative_tolerance)
        {
          current_linear_tolerance = current_residual * relative_tolerance;
        }

      // But don't let it be zero
      if (current_linear_tolerance == 0.)
        {
          current_linear_tolerance = TOLERANCE * TOLERANCE;
        }

      // At this point newton_iterate is the current guess, and
      // solution is now about to become the NEGATIVE of the next
      // Newton step.

      // Our best initial guess for the solution is zero!
      solution.zero();

      if (!quiet)
        std::cout << "Linear solve starting" << std::endl;

      PAUSE_LOG("solve()", "NewtonSolver");
      // Solve the linear system.  Two cases:
      const std::pair<unsigned int, Real> rval =
        (_system.have_matrix("Preconditioner")) ?
      // 1.) User-supplied preconditioner
        linear_solver->solve (matrix, _system.get_matrix("Preconditioner"),
                              solution, rhs, current_linear_tolerance,
                              max_linear_iterations) :
      // 2.) Use system matrix for the preconditioner
        linear_solver->solve (matrix, solution, rhs,
                              current_linear_tolerance,
                              max_linear_iterations);
      // We may need to localize a parallel solution
      _system.update ();
      RESTART_LOG("solve()", "NewtonSolver");
      // The linear solver may not have fit our constraints exactly
      _system.get_dof_map().enforce_constraints_exactly(_system);

      if (!quiet)
        std::cout << "Linear solve finished, step " << rval.first
                  << ", residual " << rval.second
                  << ", tolerance " << current_linear_tolerance
                  << std::endl;

      // Compute the l2 norm of the nonlinear update
      Real norm_delta = solution.l2_norm();

      if (!quiet)
        std::cout << "Trying full Newton step" << std::endl;
      // Take a full Newton step
      newton_iterate.add (-1., solution);
//      newton_iterate.close();

      // Check residual with full Newton step
      Real steplength = 1.;
      PAUSE_LOG("solve()", "NewtonSolver");
      _system.assembly(true, false);
      RESTART_LOG("solve()", "NewtonSolver");

      rhs.close();
      current_residual = rhs.l2_norm();

      // backtrack if necessary
      if (require_residual_reduction)
        {
          // but don't fiddle around if we've already converged
          if (test_convergence(current_residual, norm_delta))
            {
              if (!quiet)
		print_convergence(l, current_residual, norm_delta);
              break;
            }

          while (current_residual > last_residual)
            {
              // Reduce step size to 1/2, 1/4, etc.
              steplength /= 2.;
              norm_delta /= 2.;
              if (!quiet)
                std::cout << "Shrinking Newton step to "
                          << steplength << std::endl;
              newton_iterate.add (steplength, solution);
//              newton_iterate.close();

              // Check residual with fractional Newton step
              PAUSE_LOG("solve()", "NewtonSolver");
              _system.assembly (true, false);
              RESTART_LOG("solve()", "NewtonSolver");

              rhs.close();
              current_residual = rhs.l2_norm();
              if (!quiet)
                std::cout << "Current Residual: "
                          << current_residual << std::endl;

              if (steplength/2. < minsteplength &&
                  current_residual > last_residual)
                {
                  if (!quiet)
                    std::cout << "Inexact Newton step FAILED at step "
                              << l << std::endl;

                  error();
                }
            }
        }

      // Compute the l2 norm of the whole solution
      norm_total = newton_iterate.l2_norm();

      max_solution_norm = std::max(max_solution_norm, norm_total);

      // Print out information for the
      // nonlinear iterations.
      if (!quiet)
        std::cout << "  Nonlinear step: |du|/|u| = "
                  << norm_delta / norm_total
                  << ", |du| = " << norm_delta
                  << std::endl;

      // Terminate the solution iteration if the difference between
      // this iteration and the last is sufficiently small.
      if (!quiet)
        print_convergence(l, current_residual,
                          norm_delta / steplength);
      if (test_convergence(current_residual, norm_delta / steplength))
        {
          break;
        }
      if (l >= max_nonlinear_iterations - 1)
        {
          std::cout << "  Nonlinear solver DIVERGED at step " << l
                    << " with norm " << norm_total
                    << std::endl;
          error();
          continue;
        }
    } // end nonlinear loop

  // Copy the final nonlinear iterate into the current_solution,
  // for other libMesh functions that expect it

  solution = newton_iterate;
//  solution.close();

  // We may need to localize a parallel solution
  _system.update ();

  STOP_LOG("solve()", "NewtonSolver");
}

bool NewtonSolver::test_convergence(Real current_residual,
                                    Real step_norm)
{
  // We haven't converged unless we pass a convergence test
  bool has_converged = false;

  // Is our absolute residual low enough?
  if (current_residual < absolute_residual_tolerance)
    has_converged = true;

  // Is our relative residual low enough?
  if ((current_residual / max_residual_norm) <
      relative_residual_tolerance)
    has_converged = true;

  // Is our absolute Newton step size small enough?
  if (step_norm < absolute_step_tolerance)
    has_converged = true;

  // Is our relative Newton step size small enough?
  if (step_norm / max_solution_norm <
      relative_step_tolerance)
    has_converged = true;

  return has_converged;
}

void NewtonSolver::print_convergence(unsigned int step_num,
                                     Real current_residual,
                                     Real step_norm)
{
  // Is our absolute residual low enough?
  if (current_residual < absolute_residual_tolerance)
    {
      std::cout << "  Nonlinear solver converged, step " << step_num
                << ", residual " << current_residual
                << std::endl;
    }
  else if (absolute_residual_tolerance)
    {
      std::cout << "  Nonlinear solver current_residual "
                << current_residual << " > "
                << (absolute_residual_tolerance) << std::endl;
    }

  // Is our relative residual low enough?
  if ((current_residual / max_residual_norm) <
      relative_residual_tolerance)
    {
      std::cout << "  Nonlinear solver converged, step " << step_num
                << ", residual reduction "
                << current_residual / max_residual_norm
                << " < " << relative_residual_tolerance
                << std::endl;
    }
  else if (relative_residual_tolerance)
    {
      if (!quiet)
        std::cout << "  Nonlinear solver relative residual "
                  << (current_residual / max_residual_norm)
                  << " > " << relative_residual_tolerance
                  << std::endl;
    }

  // Is our absolute Newton step size small enough?
  if (step_norm < absolute_step_tolerance)
    {
      std::cout << "  Nonlinear solver converged, step " << step_num
                << ", absolute step size "
                << step_norm
                << " < " << absolute_step_tolerance
                << std::endl;
    }
  else if (absolute_step_tolerance)
    {
      std::cout << "  Nonlinear solver absolute step size "
                << step_norm
                << " > " << absolute_step_tolerance
                << std::endl;
    }

  // Is our relative Newton step size small enough?
  if (step_norm / max_solution_norm <
      relative_step_tolerance)
    {
      std::cout << "  Nonlinear solver converged, step " << step_num
                << ", relative step size "
                << (step_norm / max_solution_norm)
                << " < " << relative_step_tolerance
                << std::endl;
    }
  else if (relative_step_tolerance)
    {
      std::cout << "  Nonlinear solver relative step size "
                << (step_norm / max_solution_norm)
                << " > " << relative_step_tolerance
                << std::endl;
    }
}
<|endoftext|>


<commit_before>
#include "csv.h"

#include <sys/stat.h>
#include <iostream>
#include <cstring>
#include <stdexcept>

using namespace std;

CSV::CSV(string path)
{
    _encoding = UTF8;
    _delim = ',';
	_eof = false;

    _path = path;
	_fileSize = 0;
	_filePosition = 0;
}

void CSV::open()
{
	struct stat fileInfo;
	int error = stat(_path.c_str(), &fileInfo);

	if (error != 0)
		throw runtime_error("Could not stat file");

	_fileSize = fileInfo.st_size;

	if (_fileSize == 0)
	{
		_status = Empty;
		throw runtime_error("File is empty");
	}

	_rawBufferStartPos = 0;
	_rawBufferEndPos = 0;
	_utf8BufferStartPos = 0;
	_utf8BufferEndPos = 0;

	_stream.open(_path.c_str(), ios::in);
	if ( ! _stream.is_open())
	{
		_status = Empty;
		throw runtime_error("Could not open file");
	}

	if (readRaw())
	{
		determineEncoding();
		readUtf8();
		determineDelimiters();
	}
	else
	{
		_status = Empty;
	}

	//std::cout << "encoding : " << _encoding << " delimeters : " << _delim << "\n";
	//std::cout.flush();
}

bool CSV::readRaw()
{
	int bytesToMove = _rawBufferEndPos - _rawBufferStartPos;

	for (int i = bytesToMove - 1; i >= 0; i--)
		_rawBuffer[i] = _rawBuffer[_rawBufferStartPos + i];

	_rawBufferEndPos = bytesToMove;
	_rawBufferStartPos = 0;

	_stream.read(&_rawBuffer[_rawBufferEndPos], sizeof(_rawBuffer) - _rawBufferEndPos);
	int bytesRead = _stream.gcount();

	_filePosition += bytesRead;

	if (bytesRead == 0)
	{
		return false;
	}
	else
	{
		_rawBufferEndPos += bytesRead;
		return true;
	}
}

void CSV::determineEncoding()
{
	if (_rawBufferEndPos >= 4 && _rawBuffer[0] == -1 && _rawBuffer[1] == -2 && _rawBuffer[2] == 0 && _rawBuffer[3] == 0)
	{
		_encoding = UTF32LE;
		_rawBufferStartPos = 4;
	}
	else if (_rawBufferEndPos >= 4 && _rawBuffer[0] == 0 && _rawBuffer[1] == 0 && _rawBuffer[2] == -2 && _rawBuffer[3] == -1)
	{
		_encoding = UTF32BE;
		_rawBufferStartPos = 4;
	}
	else if (_rawBufferEndPos >= 2 && _rawBuffer[0] == -1 && _rawBuffer[1] == -2)
	{
		_encoding = UTF16LE;
		_rawBufferStartPos = 2;
	}
	else if (_rawBufferEndPos >= 2 && _rawBuffer[0] == -2 && _rawBuffer[1] == -1)
	{
		_encoding = UTF16BE;
		_rawBufferStartPos = 2;
	}
	else if (_rawBufferEndPos >= 3 && _rawBuffer[0] == -17 && _rawBuffer[1] == -69 && _rawBuffer[2] == -65)
	{
		_encoding = UTF8;
		_rawBufferStartPos = 3;
	}
	else
	{
		// tab, lf, cr, space, double-quote, single-quote, comma, semi-colon

		uint16_t utf16be[] = { 0x0009, 0x000A, 0x000D, 0x0020, 0x0022, 0x0027, 0x002C, 0x003B };
		uint16_t utf16le[] = { 0x0900, 0x0A00, 0x0D00, 0x2000, 0x2200, 0x2700, 0x2C00, 0x3B00 };

		uint16_t *buffer = (uint16_t*)&_rawBuffer[_rawBufferStartPos];
		int count = (_rawBufferEndPos - _rawBufferStartPos) / 2;

		int beCount = 0;
		int leCount = 0;

		for (int i = 0; i < count; i++)
		{
			for (int j = 0; j < 8; j++)
			{
				if (buffer[i] == utf16be[j])
				{
					beCount++;
					break;
				}
				if (buffer[i] == utf16le[j])
				{
					leCount++;
					break;
				}
			}
		}

		if (beCount > leCount)
			_encoding = UTF16LE;
		else if (leCount > 1)
			_encoding = UTF16BE;
		else
			_encoding = UTF8;

	}
}

bool CSV::readUtf8()
{
	if (_rawBufferEndPos == _rawBufferStartPos)
	{
		bool success = readRaw();

		if ( ! success)
			return false;
	}
	else if ((_rawBufferEndPos - _rawBufferStartPos) <= 2) // in case a lead surrogate was left behind
	{
		readRaw();
	}

	int bytesToMove = _utf8BufferEndPos - _utf8BufferStartPos;

	for (int i = bytesToMove - 1; i >= 0; i--)
		_utf8Buffer[i] = _utf8Buffer[_utf8BufferStartPos + i];

	_utf8BufferEndPos = bytesToMove;
	_utf8BufferStartPos = 0;

	if (_encoding == UTF16BE || _encoding == UTF16LE)
	{
		int written, read;

		bool success = utf16to8(
			&_utf8Buffer[_utf8BufferEndPos],
			&_rawBuffer[_rawBufferStartPos],
			sizeof(_utf8Buffer) - _utf8BufferEndPos,
			_rawBufferEndPos - _rawBufferStartPos,
			written,
			read,
			_encoding == UTF16BE);

		if ( ! success)
			return false;

		_utf8BufferEndPos += written;
		_rawBufferStartPos += read;
	}
	else
	{
		std::memcpy(&_utf8Buffer[_utf8BufferEndPos], &_rawBuffer[_rawBufferStartPos], _rawBufferEndPos - _rawBufferStartPos);

		_utf8BufferEndPos += _rawBufferEndPos - _rawBufferStartPos;
		_rawBufferStartPos = _rawBufferEndPos;
	}

	return true;
}

void CSV::determineDelimiters()
{
	bool inQuote = false;

	enum { COMMA = 0, SEMICOLON = 1, SPACE = 2 };

	int counts[] = { 0, 0, 0 };

	for (int i = 0; i < _utf8BufferEndPos; i++)
	{
		char ch = _utf8Buffer[i];

		switch (ch)
		{
		case '"':
			if (inQuote && i + 1 < _utf8BufferEndPos && _utf8Buffer[i + 1] == '"')
				i++;
			else
				inQuote = !inQuote;
			break;
		case ',':
			counts[COMMA]++;
			break;
		case ';':
			counts[SEMICOLON]++;
			break;
		case ' ':
			counts[SPACE]++;
			break;
		}
	}

	int maxi = 0;
	for (int i = 1; i < 3; i++)
	{
		if (counts[maxi] < counts[i])
			maxi = i;
	}

	switch (maxi)
	{
	case SEMICOLON:
		_delim = ';';
		break;
	case SPACE:
		_delim = ' ';
		break;
	default:
		_delim = ',';
		break;
	}
}

bool CSV::readLine(vector<string> &items)
{
	if (_eof)
		return false;

	if (_utf8BufferEndPos == _utf8BufferStartPos)
	{
		bool success = readUtf8();
		if ( ! success)
			return false;
	}

	bool inQuote = false;

	int i = _utf8BufferStartPos;

	while (true)
	{
		char ch = _utf8Buffer[i];

		if (ch == '"')
		{
			if (inQuote && i + 1 < _utf8BufferEndPos && _utf8Buffer[i + 1] == '"')
				i++;
			else
				inQuote = !inQuote;
		}

		if (inQuote)
		{
			// do nothing
		}
		else if (ch == _delim)
		{
			string token(&_utf8Buffer[_utf8BufferStartPos], i - _utf8BufferStartPos);
			items.push_back(token);
			_utf8BufferStartPos = i + 1;
		}
		else if (ch == '\r')
		{
			string token(&_utf8Buffer[_utf8BufferStartPos], i - _utf8BufferStartPos);
			items.push_back(token);

			if (i + 1 < _utf8BufferEndPos && _utf8Buffer[i + 1] == '\n')
				_utf8BufferStartPos = i + 2;
			else
				_utf8BufferStartPos = i + 1;

			break;
		}
		else if (ch == '\n')
		{
			string token(&_utf8Buffer[_utf8BufferStartPos], i - _utf8BufferStartPos);
			items.push_back(token);

			_utf8BufferStartPos = i + 1;
			break;
		}

		if (i >= _utf8BufferEndPos - 1)
		{
			bool success = readUtf8();
			if (success)
			{
				i = 0;
			}
			else // eof
			{
				string token(&_utf8Buffer[_utf8BufferStartPos], _utf8BufferEndPos - _utf8BufferStartPos);
				items.push_back(token);
				_eof = true;
				break;
			}
		}

		i++;
	}

	for (int i = 0; i < items.size(); i++)
	{
		string item = items.at(i);
		if (item.size() > 2 && item[0] == '"' && item[item.size()-1] == '"')
			item = item.substr(1, item.size()-2);
		items[i] = item;
	}

	return true;
}

long CSV::pos()
{
	return _filePosition;
}

long CSV::size()
{
	return _fileSize;
}

void CSV::close()
{
	_stream.close();
}

bool CSV::utf16to8(char *out, char *in, int outSize, int inSize, int &written, int &read, bool bigEndian)
{
	written = 0;
	read = 0;
	int i;
	bool success;

	while (true)
	{
		int bytesLeftToRead = inSize - read;
		int roomLeftToWrite = outSize - written;

		uint32_t ch;

		int justRead;
		int justWritten;

		success = utf16to32(ch, &in[read], bytesLeftToRead, justRead, bigEndian);
		if ( ! success)
			break;

		success = utf32to8(&out[written], ch, roomLeftToWrite, justWritten);
		if ( ! success)
			break;

		read += justRead;
		written += justWritten;

	}

	return read > 0 && written > 0;

}

bool CSV::utf16to32(uint32_t &out, char *in, int inSize, int& bytesRead, bool bigEndian)
{

#define UNI_SUR_HIGH_START      (uint32_t)0xD800
#define UNI_SUR_HIGH_END        (uint32_t)0xDBFF
#define UNI_SUR_LOW_START       (uint32_t)0xDC00
#define UNI_SUR_LOW_END         (uint32_t)0xDFFF

#define UNI_HALF_SHIFT          (uint32_t)10
#define UNI_HALF_BASE           (uint32_t)0x0010000UL
#define UNI_HALF_MASK           (uint32_t)0x3FFUL

	if (inSize < 2)
		return false;

	uint32_t upper;

	if (bigEndian)
	{
		upper = in[0];
		upper <<= 8;
		upper |= in[1];
	}
	else
	{
		upper = *(uint16_t*)(in);
	}

	if (*in >= UNI_SUR_HIGH_START && *in <= UNI_SUR_LOW_START)
	{
		if (inSize < 4)
			return false;

		uint32_t lower;

		if (bigEndian)
		{
			lower = in[3];
			lower <<= 8;
			lower |= in[2];
		}
		else
		{
			lower = *(uint16_t*)(in + 2);
		}

		if (lower >= UNI_SUR_LOW_START && lower <= UNI_SUR_LOW_END)
		{
			out = ((upper - UNI_SUR_HIGH_START) << UNI_HALF_SHIFT) + (lower - UNI_SUR_LOW_START) + UNI_HALF_BASE;
		}

		bytesRead = 4;
		return true;
	}
	else
	{
		out = upper;

		bytesRead = 2;
		return true;
	}
}

bool CSV::utf32to8(char *out, uint32_t in, int outSize, int &bytesWritten)
{
	int width;
	const unsigned char FIRST_BYTE_MARK[7] = {0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC};

	if (in < 0x80)
	{
		width = 1;
	}
	else if (in < 0x800)
	{
		width = 2;
	}
	else if (in < 0x10000)
	{
		width = 3;
	}
	else if (in < 0x110000)
	{
		width = 4;
	}
	else
	{
		in = 0xFFFD;  // replacement character
		width = 3;
	}

	if (width > outSize)
		return false;

	switch (width)
	{
	case 4:
		out[3] = (char)((in | 0x80) & 0xBF);
		in >>= 6;
	case 3:
		out[2] = (char)((in | 0x80) & 0xBF);
		in >>= 6;
	case 2:
		out[1] = (char)((in | 0x80) & 0xBF);
		in >>= 6;
	case 1:
		out[0] = (char)(in | FIRST_BYTE_MARK[width]);
	}

	bytesWritten = width;

	return true;
}
<commit_msg>Added support for tab separated .csv files<commit_after>
#include "csv.h"

#include <sys/stat.h>
#include <iostream>
#include <cstring>
#include <stdexcept>

using namespace std;

CSV::CSV(string path)
{
    _encoding = UTF8;
    _delim = ',';
	_eof = false;

    _path = path;
	_fileSize = 0;
	_filePosition = 0;
}

void CSV::open()
{
	struct stat fileInfo;
	int error = stat(_path.c_str(), &fileInfo);

	if (error != 0)
		throw runtime_error("Could not stat file");

	_fileSize = fileInfo.st_size;

	if (_fileSize == 0)
	{
		_status = Empty;
		throw runtime_error("File is empty");
	}

	_rawBufferStartPos = 0;
	_rawBufferEndPos = 0;
	_utf8BufferStartPos = 0;
	_utf8BufferEndPos = 0;

	_stream.open(_path.c_str(), ios::in);
	if ( ! _stream.is_open())
	{
		_status = Empty;
		throw runtime_error("Could not open file");
	}

	if (readRaw())
	{
		determineEncoding();
		readUtf8();
		determineDelimiters();
	}
	else
	{
		_status = Empty;
	}

	//std::cout << "encoding : " << _encoding << " delimeters : " << _delim << "\n";
	//std::cout.flush();
}

bool CSV::readRaw()
{
	int bytesToMove = _rawBufferEndPos - _rawBufferStartPos;

	for (int i = bytesToMove - 1; i >= 0; i--)
		_rawBuffer[i] = _rawBuffer[_rawBufferStartPos + i];

	_rawBufferEndPos = bytesToMove;
	_rawBufferStartPos = 0;

	_stream.read(&_rawBuffer[_rawBufferEndPos], sizeof(_rawBuffer) - _rawBufferEndPos);
	int bytesRead = _stream.gcount();

	_filePosition += bytesRead;

	if (bytesRead == 0)
	{
		return false;
	}
	else
	{
		_rawBufferEndPos += bytesRead;
		return true;
	}
}

void CSV::determineEncoding()
{
	if (_rawBufferEndPos >= 4 && _rawBuffer[0] == -1 && _rawBuffer[1] == -2 && _rawBuffer[2] == 0 && _rawBuffer[3] == 0)
	{
		_encoding = UTF32LE;
		_rawBufferStartPos = 4;
	}
	else if (_rawBufferEndPos >= 4 && _rawBuffer[0] == 0 && _rawBuffer[1] == 0 && _rawBuffer[2] == -2 && _rawBuffer[3] == -1)
	{
		_encoding = UTF32BE;
		_rawBufferStartPos = 4;
	}
	else if (_rawBufferEndPos >= 2 && _rawBuffer[0] == -1 && _rawBuffer[1] == -2)
	{
		_encoding = UTF16LE;
		_rawBufferStartPos = 2;
	}
	else if (_rawBufferEndPos >= 2 && _rawBuffer[0] == -2 && _rawBuffer[1] == -1)
	{
		_encoding = UTF16BE;
		_rawBufferStartPos = 2;
	}
	else if (_rawBufferEndPos >= 3 && _rawBuffer[0] == -17 && _rawBuffer[1] == -69 && _rawBuffer[2] == -65)
	{
		_encoding = UTF8;
		_rawBufferStartPos = 3;
	}
	else
	{
		// tab, lf, cr, space, double-quote, single-quote, comma, semi-colon

		uint16_t utf16be[] = { 0x0009, 0x000A, 0x000D, 0x0020, 0x0022, 0x0027, 0x002C, 0x003B };
		uint16_t utf16le[] = { 0x0900, 0x0A00, 0x0D00, 0x2000, 0x2200, 0x2700, 0x2C00, 0x3B00 };

		uint16_t *buffer = (uint16_t*)&_rawBuffer[_rawBufferStartPos];
		int count = (_rawBufferEndPos - _rawBufferStartPos) / 2;

		int beCount = 0;
		int leCount = 0;

		for (int i = 0; i < count; i++)
		{
			for (int j = 0; j < 8; j++)
			{
				if (buffer[i] == utf16be[j])
				{
					beCount++;
					break;
				}
				if (buffer[i] == utf16le[j])
				{
					leCount++;
					break;
				}
			}
		}

		if (beCount > leCount)
			_encoding = UTF16LE;
		else if (leCount > 1)
			_encoding = UTF16BE;
		else
			_encoding = UTF8;

	}
}

bool CSV::readUtf8()
{
	if (_rawBufferEndPos == _rawBufferStartPos)
	{
		bool success = readRaw();

		if ( ! success)
			return false;
	}
	else if ((_rawBufferEndPos - _rawBufferStartPos) <= 2) // in case a lead surrogate was left behind
	{
		readRaw();
	}

	int bytesToMove = _utf8BufferEndPos - _utf8BufferStartPos;

	for (int i = bytesToMove - 1; i >= 0; i--)
		_utf8Buffer[i] = _utf8Buffer[_utf8BufferStartPos + i];

	_utf8BufferEndPos = bytesToMove;
	_utf8BufferStartPos = 0;

	if (_encoding == UTF16BE || _encoding == UTF16LE)
	{
		int written, read;

		bool success = utf16to8(
			&_utf8Buffer[_utf8BufferEndPos],
			&_rawBuffer[_rawBufferStartPos],
			sizeof(_utf8Buffer) - _utf8BufferEndPos,
			_rawBufferEndPos - _rawBufferStartPos,
			written,
			read,
			_encoding == UTF16BE);

		if ( ! success)
			return false;

		_utf8BufferEndPos += written;
		_rawBufferStartPos += read;
	}
	else
	{
		std::memcpy(&_utf8Buffer[_utf8BufferEndPos], &_rawBuffer[_rawBufferStartPos], _rawBufferEndPos - _rawBufferStartPos);

		_utf8BufferEndPos += _rawBufferEndPos - _rawBufferStartPos;
		_rawBufferStartPos = _rawBufferEndPos;
	}

	return true;
}

void CSV::determineDelimiters()
{
	bool inQuote = false;

	enum { COMMA = 0, SEMICOLON = 1, SPACE = 2, TAB = 3 };

	int counts[] = { 0, 0, 0, 0 };

	for (int i = 0; i < _utf8BufferEndPos; i++)
	{
		char ch = _utf8Buffer[i];

		switch (ch)
		{
		case '"':
			if (inQuote && i + 1 < _utf8BufferEndPos && _utf8Buffer[i + 1] == '"')
				i++;
			else
				inQuote = !inQuote;
			break;
		case ',':
			counts[COMMA]++;
			break;
		case ';':
			counts[SEMICOLON]++;
			break;
		case ' ':
			counts[SPACE]++;
			break;
		case '\t':
			counts[TAB]++;
			break;
		}
	}

	int maxi = 0;
	for (int i = 1; i < 4; i++)
	{
		if (counts[maxi] < counts[i])
			maxi = i;
	}

	switch (maxi)
	{
	case SEMICOLON:
		_delim = ';';
		break;
	case SPACE:
		_delim = ' ';
		break;
	case TAB:
		_delim = '\t';
		break;
	default:
		_delim = ',';
		break;
	}
}

bool CSV::readLine(vector<string> &items)
{
	if (_eof)
		return false;

	if (_utf8BufferEndPos == _utf8BufferStartPos)
	{
		bool success = readUtf8();
		if ( ! success)
			return false;
	}

	bool inQuote = false;

	int i = _utf8BufferStartPos;

	while (true)
	{
		char ch = _utf8Buffer[i];

		if (ch == '"')
		{
			if (inQuote && i + 1 < _utf8BufferEndPos && _utf8Buffer[i + 1] == '"')
				i++;
			else
				inQuote = !inQuote;
		}

		if (inQuote)
		{
			// do nothing
		}
		else if (ch == _delim)
		{
			string token(&_utf8Buffer[_utf8BufferStartPos], i - _utf8BufferStartPos);
			items.push_back(token);
			_utf8BufferStartPos = i + 1;
		}
		else if (ch == '\r')
		{
			string token(&_utf8Buffer[_utf8BufferStartPos], i - _utf8BufferStartPos);
			items.push_back(token);

			if (i + 1 < _utf8BufferEndPos && _utf8Buffer[i + 1] == '\n')
				_utf8BufferStartPos = i + 2;
			else
				_utf8BufferStartPos = i + 1;

			break;
		}
		else if (ch == '\n')
		{
			string token(&_utf8Buffer[_utf8BufferStartPos], i - _utf8BufferStartPos);
			items.push_back(token);

			_utf8BufferStartPos = i + 1;
			break;
		}

		if (i >= _utf8BufferEndPos - 1)
		{
			bool success = readUtf8();
			if (success)
			{
				i = 0;
			}
			else // eof
			{
				string token(&_utf8Buffer[_utf8BufferStartPos], _utf8BufferEndPos - _utf8BufferStartPos);
				items.push_back(token);
				_eof = true;
				break;
			}
		}

		i++;
	}

	for (int i = 0; i < items.size(); i++)
	{
		string item = items.at(i);
		if (item.size() > 2 && item[0] == '"' && item[item.size()-1] == '"')
			item = item.substr(1, item.size()-2);
		items[i] = item;
	}

	return true;
}

long CSV::pos()
{
	return _filePosition;
}

long CSV::size()
{
	return _fileSize;
}

void CSV::close()
{
	_stream.close();
}

bool CSV::utf16to8(char *out, char *in, int outSize, int inSize, int &written, int &read, bool bigEndian)
{
	written = 0;
	read = 0;
	int i;
	bool success;

	while (true)
	{
		int bytesLeftToRead = inSize - read;
		int roomLeftToWrite = outSize - written;

		uint32_t ch;

		int justRead;
		int justWritten;

		success = utf16to32(ch, &in[read], bytesLeftToRead, justRead, bigEndian);
		if ( ! success)
			break;

		success = utf32to8(&out[written], ch, roomLeftToWrite, justWritten);
		if ( ! success)
			break;

		read += justRead;
		written += justWritten;

	}

	return read > 0 && written > 0;

}

bool CSV::utf16to32(uint32_t &out, char *in, int inSize, int& bytesRead, bool bigEndian)
{

#define UNI_SUR_HIGH_START      (uint32_t)0xD800
#define UNI_SUR_HIGH_END        (uint32_t)0xDBFF
#define UNI_SUR_LOW_START       (uint32_t)0xDC00
#define UNI_SUR_LOW_END         (uint32_t)0xDFFF

#define UNI_HALF_SHIFT          (uint32_t)10
#define UNI_HALF_BASE           (uint32_t)0x0010000UL
#define UNI_HALF_MASK           (uint32_t)0x3FFUL

	if (inSize < 2)
		return false;

	uint32_t upper;

	if (bigEndian)
	{
		upper = in[0];
		upper <<= 8;
		upper |= in[1];
	}
	else
	{
		upper = *(uint16_t*)(in);
	}

	if (*in >= UNI_SUR_HIGH_START && *in <= UNI_SUR_LOW_START)
	{
		if (inSize < 4)
			return false;

		uint32_t lower;

		if (bigEndian)
		{
			lower = in[3];
			lower <<= 8;
			lower |= in[2];
		}
		else
		{
			lower = *(uint16_t*)(in + 2);
		}

		if (lower >= UNI_SUR_LOW_START && lower <= UNI_SUR_LOW_END)
		{
			out = ((upper - UNI_SUR_HIGH_START) << UNI_HALF_SHIFT) + (lower - UNI_SUR_LOW_START) + UNI_HALF_BASE;
		}

		bytesRead = 4;
		return true;
	}
	else
	{
		out = upper;

		bytesRead = 2;
		return true;
	}
}

bool CSV::utf32to8(char *out, uint32_t in, int outSize, int &bytesWritten)
{
	int width;
	const unsigned char FIRST_BYTE_MARK[7] = {0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC};

	if (in < 0x80)
	{
		width = 1;
	}
	else if (in < 0x800)
	{
		width = 2;
	}
	else if (in < 0x10000)
	{
		width = 3;
	}
	else if (in < 0x110000)
	{
		width = 4;
	}
	else
	{
		in = 0xFFFD;  // replacement character
		width = 3;
	}

	if (width > outSize)
		return false;

	switch (width)
	{
	case 4:
		out[3] = (char)((in | 0x80) & 0xBF);
		in >>= 6;
	case 3:
		out[2] = (char)((in | 0x80) & 0xBF);
		in >>= 6;
	case 2:
		out[1] = (char)((in | 0x80) & 0xBF);
		in >>= 6;
	case 1:
		out[0] = (char)(in | FIRST_BYTE_MARK[width]);
	}

	bytesWritten = width;

	return true;
}
<|endoftext|>


<commit_before>#define DEBUG 1
/**
 * File    : E2.cpp
 * Author  : Kazune Takahashi
 * Created : 2020/1/20 0:19:15
 * Powered by Visual Studio Code
 */
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cctype>
#include <chrono>
#include <cmath>
#include <complex>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <random>
#include <set>
#include <stack>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <vector>
// ----- boost -----
#include <boost/rational.hpp>
#include <boost/multiprecision/cpp_int.hpp>
// ----- using directives and manipulations -----
using namespace std;
using boost::rational;
using boost::multiprecision::cpp_int;
using ll = long long;
template <typename T>
using max_heap = priority_queue<T>;
template <typename T>
using min_heap = priority_queue<T, vector<T>, greater<T>>;
// ----- constexpr for Mint and Combination -----
constexpr ll MOD{1000000007LL};
// constexpr ll MOD{998244353LL}; // be careful
constexpr ll MAX_SIZE{3000010LL};
// constexpr ll MAX_SIZE{30000010LL}; // if 10^7 is needed
// ----- ch_max and ch_min -----
template <typename T>
void ch_max(T &left, T right)
{
  if (left < right)
  {
    left = right;
  }
}
template <typename T>
void ch_min(T &left, T right)
{
  if (left > right)
  {
    left = right;
  }
}
// ----- Mint -----
template <ll MOD = MOD>
class Mint
{
public:
  ll x;
  Mint() : x{0LL} {}
  Mint(ll x) : x{(x % MOD + MOD) % MOD} {}
  Mint operator-() const { return x ? MOD - x : 0; }
  Mint &operator+=(const Mint &a)
  {
    if ((x += a.x) >= MOD)
    {
      x -= MOD;
    }
    return *this;
  }
  Mint &operator-=(const Mint &a) { return *this += -a; }
  Mint &operator*=(const Mint &a)
  {
    (x *= a.x) %= MOD;
    return *this;
  }
  Mint &operator/=(const Mint &a)
  {
    Mint b{a};
    return *this *= b.power(MOD - 2);
  }
  Mint operator+(const Mint &a) const { return Mint(*this) += a; }
  Mint operator-(const Mint &a) const { return Mint(*this) -= a; }
  Mint operator*(const Mint &a) const { return Mint(*this) *= a; }
  Mint operator/(const Mint &a) const { return Mint(*this) /= a; }
  bool operator<(const Mint &a) const { return x < a.x; }
  bool operator<=(const Mint &a) const { return x <= a.x; }
  bool operator>(const Mint &a) const { return x > a.x; }
  bool operator>=(const Mint &a) const { return x >= a.x; }
  bool operator==(const Mint &a) const { return x == a.x; }
  bool operator!=(const Mint &a) const { return !(*this == a); }
  const Mint power(ll N)
  {
    if (N == 0)
    {
      return 1;
    }
    else if (N % 2 == 1)
    {
      return *this * power(N - 1);
    }
    else
    {
      Mint half = power(N / 2);
      return half * half;
    }
  }
};
template <ll MOD>
Mint<MOD> operator+(ll lhs, const Mint<MOD> &rhs)
{
  return rhs + lhs;
}
template <ll MOD>
Mint<MOD> operator-(ll lhs, const Mint<MOD> &rhs)
{
  return -rhs + lhs;
}
template <ll MOD>
Mint<MOD> operator*(ll lhs, const Mint<MOD> &rhs)
{
  return rhs * lhs;
}
template <ll MOD>
Mint<MOD> operator/(ll lhs, const Mint<MOD> &rhs)
{
  return Mint<MOD>{lhs} / rhs;
}
template <ll MOD>
istream &operator>>(istream &stream, Mint<MOD> &a)
{
  return stream >> a.x;
}
template <ll MOD>
ostream &operator<<(ostream &stream, const Mint<MOD> &a)
{
  return stream << a.x;
}
// ----- Combination -----
template <ll MOD = MOD, ll MAX_SIZE = MAX_SIZE>
class Combination
{
public:
  vector<Mint<MOD>> inv, fact, factinv;
  Combination() : inv(MAX_SIZE), fact(MAX_SIZE), factinv(MAX_SIZE)
  {
    inv[1] = 1;
    for (auto i = 2LL; i < MAX_SIZE; i++)
    {
      inv[i] = (-inv[MOD % i]) * (MOD / i);
    }
    fact[0] = factinv[0] = 1;
    for (auto i = 1LL; i < MAX_SIZE; i++)
    {
      fact[i] = Mint<MOD>(i) * fact[i - 1];
      factinv[i] = inv[i] * factinv[i - 1];
    }
  }
  Mint<MOD> operator()(int n, int k)
  {
    if (n >= 0 && k >= 0 && n - k >= 0)
    {
      return fact[n] * factinv[k] * factinv[n - k];
    }
    return 0;
  }
  Mint<MOD> catalan(int x, int y)
  {
    return (*this)(x + y, y) - (*this)(x + y, y - 1);
  }
};
// ----- for C++14 -----
using mint = Mint<MOD>;
using combination = Combination<MOD, MAX_SIZE>;
template <typename T>
T gcd(T x, T y) { return y ? gcd(y, x % y) : x; }
template <typename T>
T lcm(T x, T y) { return x / gcd(x, y) * y; }
// ----- frequently used constexpr -----
// constexpr double epsilon{1e-10};
// constexpr ll infty{1000000000000000LL};
// constexpr int dx[4] = {1, 0, -1, 0};
// constexpr int dy[4] = {0, 1, 0, -1};
// ----- Yes() and No() -----
void Yes()
{
  cout << "Yes" << endl;
  exit(0);
}
void No()
{
  cout << "No" << endl;
  exit(0);
}
// ----- main() -----

class Sieve
{
  static constexpr ll MAX_SIZE{1000010LL};
  ll N;
  vector<bool> isprime;
  vector<ll> prime_nums;

public:
  Sieve(ll N = MAX_SIZE) : N{N}, isprime(N, true), prime_nums{}
  {
    isprime[0] = isprime[1] = false;
    for (auto i = 2; i < N; i++)
    {
      if (isprime[i])
      {
        prime_nums.push_back(i);
        for (auto j = i * i; j < N; j += i)
        {
          isprime[j] = false;
        }
      }
    }
  }

  bool is_prime(ll x) const
  { // 2 \leq x \leq MAX_SIZE^2
    if (x < N)
    {
      return isprime[x];
    }
    for (auto e : prime_nums)
    {
      if (x % e == 0)
        return false;
    }
    return true;
  }

  vector<ll> const &primes() const
  {
    return prime_nums;
  }

  vector<ll> factor_list(ll x) const
  {
    if (x < 2)
    {
      return {};
    }
    vector<ll> res;
    auto it{prime_nums.begin()};
    while (x != 1 && it != prime_nums.end())
    {
      if (x % *it == 0)
      {
        res.push_back(*it);
        x /= *it;
      }
      else
      {
        ++it;
      }
    }
    if (x != 1)
    {
      res.push_back(x);
    }
    return res;
  }

  vector<tuple<ll, ll>> factor(ll x) const
  {
    if (x < 2)
    {
      return {};
    }
    auto factors{factor_list(x)};
    vector<tuple<ll, ll>> res{make_tuple(factors[0], 0)};
    for (auto x : factors)
    {
      if (x == get<0>(res.back()))
      {
        get<1>(res.back())++;
      }
      else
      {
        res.emplace_back(x, 1);
      }
    }
    return res;
  }
};

void solve()
{
  Sieve sieve;
  int N;
  cin >> N;
  vector<ll> A(N);
  for (auto i = 0; i < N; ++i)
  {
    cin >> A[i];
  }
  map<ll, ll> M;
  for (auto i = 0; i < N; ++i)
  {
    auto f{sieve.factor(A[i])};
    for (auto p : f)
    {
      if (M.find(get<0>(p)) == M.end())
      {
        M[get<0>(p)] = get<1>(p);
      }
      else
      {
        ch_max(M[get<0>(p)], get<1>(p));
      }
    }
  }
  mint lcm{1};
  for (auto p : M)
  {
    for (auto i = 0; i < get<1>(p); ++i)
    {
      lcm *= get<0>(p);
    }
  }
  mint ans{0};
  for (auto i = 0; i < N; ++i)
  {
    mint B{lcm / A[i]};
    ans += B;
  }
  cout << ans << endl;
}

int main()
{
  solve();
}
<commit_msg>submit E2.cpp to 'E - Flatten' (abc152) [C++14 (GCC 5.4.1)]<commit_after>#define DEBUG 1
/**
 * File    : E2.cpp
 * Author  : Kazune Takahashi
 * Created : 2020/1/20 0:19:15
 * Powered by Visual Studio Code
 */
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cctype>
#include <chrono>
#include <cmath>
#include <complex>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <random>
#include <set>
#include <stack>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <vector>
// ----- boost -----
#include <boost/rational.hpp>
#include <boost/multiprecision/cpp_int.hpp>
// ----- using directives and manipulations -----
using namespace std;
using boost::rational;
using boost::multiprecision::cpp_int;
using ll = long long;
template <typename T>
using max_heap = priority_queue<T>;
template <typename T>
using min_heap = priority_queue<T, vector<T>, greater<T>>;
// ----- constexpr for Mint and Combination -----
constexpr ll MOD{1000000007LL};
// constexpr ll MOD{998244353LL}; // be careful
constexpr ll MAX_SIZE{3000010LL};
// constexpr ll MAX_SIZE{30000010LL}; // if 10^7 is needed
// ----- ch_max and ch_min -----
template <typename T>
void ch_max(T &left, T right)
{
  if (left < right)
  {
    left = right;
  }
}
template <typename T>
void ch_min(T &left, T right)
{
  if (left > right)
  {
    left = right;
  }
}
// ----- Mint -----
template <ll MOD = MOD>
class Mint
{
public:
  ll x;
  Mint() : x{0LL} {}
  Mint(ll x) : x{(x % MOD + MOD) % MOD} {}
  Mint operator-() const { return x ? MOD - x : 0; }
  Mint &operator+=(const Mint &a)
  {
    if ((x += a.x) >= MOD)
    {
      x -= MOD;
    }
    return *this;
  }
  Mint &operator-=(const Mint &a) { return *this += -a; }
  Mint &operator*=(const Mint &a)
  {
    (x *= a.x) %= MOD;
    return *this;
  }
  Mint &operator/=(const Mint &a)
  {
    Mint b{a};
    return *this *= b.power(MOD - 2);
  }
  Mint operator+(const Mint &a) const { return Mint(*this) += a; }
  Mint operator-(const Mint &a) const { return Mint(*this) -= a; }
  Mint operator*(const Mint &a) const { return Mint(*this) *= a; }
  Mint operator/(const Mint &a) const { return Mint(*this) /= a; }
  bool operator<(const Mint &a) const { return x < a.x; }
  bool operator<=(const Mint &a) const { return x <= a.x; }
  bool operator>(const Mint &a) const { return x > a.x; }
  bool operator>=(const Mint &a) const { return x >= a.x; }
  bool operator==(const Mint &a) const { return x == a.x; }
  bool operator!=(const Mint &a) const { return !(*this == a); }
  const Mint power(ll N)
  {
    if (N == 0)
    {
      return 1;
    }
    else if (N % 2 == 1)
    {
      return *this * power(N - 1);
    }
    else
    {
      Mint half = power(N / 2);
      return half * half;
    }
  }
};
template <ll MOD>
Mint<MOD> operator+(ll lhs, const Mint<MOD> &rhs)
{
  return rhs + lhs;
}
template <ll MOD>
Mint<MOD> operator-(ll lhs, const Mint<MOD> &rhs)
{
  return -rhs + lhs;
}
template <ll MOD>
Mint<MOD> operator*(ll lhs, const Mint<MOD> &rhs)
{
  return rhs * lhs;
}
template <ll MOD>
Mint<MOD> operator/(ll lhs, const Mint<MOD> &rhs)
{
  return Mint<MOD>{lhs} / rhs;
}
template <ll MOD>
istream &operator>>(istream &stream, Mint<MOD> &a)
{
  return stream >> a.x;
}
template <ll MOD>
ostream &operator<<(ostream &stream, const Mint<MOD> &a)
{
  return stream << a.x;
}
// ----- Combination -----
template <ll MOD = MOD, ll MAX_SIZE = MAX_SIZE>
class Combination
{
public:
  vector<Mint<MOD>> inv, fact, factinv;
  Combination() : inv(MAX_SIZE), fact(MAX_SIZE), factinv(MAX_SIZE)
  {
    inv[1] = 1;
    for (auto i = 2LL; i < MAX_SIZE; i++)
    {
      inv[i] = (-inv[MOD % i]) * (MOD / i);
    }
    fact[0] = factinv[0] = 1;
    for (auto i = 1LL; i < MAX_SIZE; i++)
    {
      fact[i] = Mint<MOD>(i) * fact[i - 1];
      factinv[i] = inv[i] * factinv[i - 1];
    }
  }
  Mint<MOD> operator()(int n, int k)
  {
    if (n >= 0 && k >= 0 && n - k >= 0)
    {
      return fact[n] * factinv[k] * factinv[n - k];
    }
    return 0;
  }
  Mint<MOD> catalan(int x, int y)
  {
    return (*this)(x + y, y) - (*this)(x + y, y - 1);
  }
};
// ----- for C++14 -----
using mint = Mint<MOD>;
using combination = Combination<MOD, MAX_SIZE>;
template <typename T>
T gcd(T x, T y) { return y ? gcd(y, x % y) : x; }
template <typename T>
T lcm(T x, T y) { return x / gcd(x, y) * y; }
// ----- frequently used constexpr -----
// constexpr double epsilon{1e-10};
// constexpr ll infty{1000000000000000LL};
// constexpr int dx[4] = {1, 0, -1, 0};
// constexpr int dy[4] = {0, 1, 0, -1};
// ----- Yes() and No() -----
void Yes()
{
  cout << "Yes" << endl;
  exit(0);
}
void No()
{
  cout << "No" << endl;
  exit(0);
}
// ----- main() -----

class Sieve
{
  static constexpr ll MAX_SIZE{1000010LL};
  ll N;
  vector<bool> isprime;
  vector<ll> prime_nums;

public:
  Sieve(ll N = MAX_SIZE) : N{N}, isprime(N, true), prime_nums{}
  {
    isprime[0] = isprime[1] = false;
    for (auto i = 2; i < N; i++)
    {
      if (isprime[i])
      {
        prime_nums.push_back(i);
        for (auto j = 2 * i; j < N; j += i)
        {
          isprime[j] = false;
        }
      }
    }
  }

  bool is_prime(ll x) const
  { // 2 \leq x \leq MAX_SIZE^2
    if (x < N)
    {
      return isprime[x];
    }
    for (auto e : prime_nums)
    {
      if (x % e == 0)
        return false;
    }
    return true;
  }

  vector<ll> const &primes() const
  {
    return prime_nums;
  }

  vector<ll> factor_list(ll x) const
  {
    if (x < 2)
    {
      return {};
    }
    vector<ll> res;
    auto it{prime_nums.begin()};
    while (x != 1 && it != prime_nums.end())
    {
      if (x % *it == 0)
      {
        res.push_back(*it);
        x /= *it;
      }
      else
      {
        ++it;
      }
    }
    if (x != 1)
    {
      res.push_back(x);
    }
    return res;
  }

  vector<tuple<ll, ll>> factor(ll x) const
  {
    if (x < 2)
    {
      return {};
    }
    auto factors{factor_list(x)};
    vector<tuple<ll, ll>> res{make_tuple(factors[0], 0)};
    for (auto x : factors)
    {
      if (x == get<0>(res.back()))
      {
        get<1>(res.back())++;
      }
      else
      {
        res.emplace_back(x, 1);
      }
    }
    return res;
  }
};

void solve()
{
  Sieve sieve;
  int N;
  cin >> N;
  vector<ll> A(N);
  for (auto i = 0; i < N; ++i)
  {
    cin >> A[i];
  }
  map<ll, ll> M;
  for (auto i = 0; i < N; ++i)
  {
    auto f{sieve.factor(A[i])};
    for (auto p : f)
    {
      if (M.find(get<0>(p)) == M.end())
      {
        M[get<0>(p)] = get<1>(p);
      }
      else
      {
        ch_max(M[get<0>(p)], get<1>(p));
      }
    }
  }
  mint lcm{1};
  for (auto p : M)
  {
    for (auto i = 0; i < get<1>(p); ++i)
    {
      lcm *= get<0>(p);
    }
  }
  mint ans{0};
  for (auto i = 0; i < N; ++i)
  {
    mint B{lcm / A[i]};
    ans += B;
  }
  cout << ans << endl;
}

int main()
{
  solve();
}
<|endoftext|>


<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2013 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#include <mapnik/text/text_layout.hpp>
#include <mapnik/text/text_properties.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/debug.hpp>
#include <mapnik/text/harfbuzz_shaper.hpp>

// ICU
#include <unicode/brkiter.h>

namespace mapnik
{

// Output is centered around (0,0)
static void rotated_box2d(box2d<double> & box, rotation const& rot, pixel_position const& center, double width, double height)
{
    double half_width, half_height;
    if (rot.sin == 0 && rot.cos == 1.)
    {
        half_width  = width / 2.;
        half_height = height / 2.;
    }
    else
    {
        half_width  = (width * rot.cos + height * rot.sin) /2.;
        half_height = (width * rot.sin + height * rot.cos) /2.;
    }
    box.init(center.x - half_width, center.y - half_height, center.x + half_width, center.y + half_height);
}

pixel_position pixel_position::rotate(rotation const& rot) const
{
    return pixel_position(x * rot.cos - y * rot.sin, x * rot.sin + y * rot.cos);
}

text_layout::text_layout(face_manager_freetype & font_manager, double scale_factor, text_layout_properties const& properties)
    : font_manager_(font_manager),
      scale_factor_(scale_factor),
      itemizer_(),
      width_map_(),
      width_(0.0),
      height_(0.0),
      glyphs_count_(0),
      lines_(),
      properties_(properties) {}

void text_layout::add_text(mapnik::value_unicode_string const& str, evaluated_format_properties_ptr format)
{
    itemizer_.add_text(str, format);
}

void text_layout::add_child(text_layout_ptr const& child_layout)
{
    child_layout_list_.push_back(child_layout);
}

mapnik::value_unicode_string const& text_layout::text() const
{
    return itemizer_.text();
}

void text_layout::layout()
{
    unsigned num_lines = itemizer_.num_lines();
    for (unsigned i = 0; i < num_lines; ++i)
    {
        std::pair<unsigned, unsigned> line_limits = itemizer_.line(i);
        text_line line(line_limits.first, line_limits.second);
        //Break line if neccessary
        break_line(line, wrap_width_ * scale_factor_, text_ratio_, wrap_before_);
    }

    init_auto_alignment();

    // Find text origin.
    displacement_ = scale_factor_ * displacement_ + alignment_offset();
    if (rotate_displacement_) displacement_ = displacement_.rotate(!orientation_);
    // Find layout bounds, expanded for rotation
    rotated_box2d(bounds_, orientation_, displacement_, width_, height_);
}

// In the Unicode string characters are always stored in logical order.
// This makes line breaking easy. One word is added to the current line at a time. Once the line is too long
// we either go back one step or inset the line break at the current position (depending on "wrap_before" setting).
// At the end everything that is left over is added as the final line.
void text_layout::break_line(text_line & line, double wrap_width, unsigned text_ratio, bool wrap_before)
{
    shape_text(line);
    if (!wrap_width || line.width() < wrap_width)
    {
        add_line(line);
        return;

    }
    if (text_ratio)
    {
        double wrap_at;
        double string_width = line.width();
        double string_height = line.line_height();
        for (double i = 1.0; ((wrap_at = string_width/i)/(string_height*i)) > text_ratio && (string_width/i) > wrap_width; i += 1.0) ;
        wrap_width = wrap_at;
    }

    mapnik::value_unicode_string const& text = itemizer_.text();
    Locale locale; // TODO: Is the default constructor correct?
    UErrorCode status = U_ZERO_ERROR;
    BreakIterator *breakitr = BreakIterator::createLineInstance(locale, status);

    // Not breaking the text if an error occurs is probably the best thing we can do.
    // https://github.com/mapnik/mapnik/issues/2072
    if (!U_SUCCESS(status))
    {
        add_line(line);
        MAPNIK_LOG_ERROR(text_layout) << " could not create BreakIterator: " << u_errorName(status);
        return;
    }

    breakitr->setText(text);

    double current_line_length = 0;
    int last_break_position = static_cast<int>(line.first_char());
    for (unsigned i=line.first_char(); i < line.last_char(); ++i)
    {
        // TODO: character_spacing
        std::map<unsigned, double>::const_iterator width_itr = width_map_.find(i);
        if (width_itr != width_map_.end())
        {
            current_line_length += width_itr->second;
        }
        if (current_line_length <= wrap_width) continue;

        int break_position = wrap_before ? breakitr->preceding(i) : breakitr->following(i);
        // following() returns a break position after the last word. So DONE should only be returned
        // when calling preceding.
        if (break_position <= last_break_position || break_position == static_cast<int>(BreakIterator::DONE))
        {
            // A single word is longer than the maximum line width.
            // Violate line width requirement and choose next break position
            break_position = breakitr->following(i);
            if (break_position == static_cast<int>(BreakIterator::DONE))
            {
                break_position = line.last_char();
                MAPNIK_LOG_ERROR(text_layout) << "Unexpected result in break_line. Trying to recover...\n";
            }
        }
        // Break iterator operates on the whole string, while we only look at one line. So we need to
        // clamp break values.
        if (break_position < static_cast<int>(line.first_char()))
        {
            break_position = line.first_char();
        }
        if (break_position > static_cast<int>(line.last_char()))
        {
            break_position = line.last_char();
        }

        text_line new_line(last_break_position, break_position);
        clear_cluster_widths(last_break_position, break_position);
        shape_text(new_line);
        add_line(new_line);
        last_break_position = break_position;
        i = break_position - 1;
        current_line_length = 0;
    }
    if (last_break_position == static_cast<int>(line.first_char()))
    {
        // No line breaks => no reshaping required
        add_line(line);
    }
    else if (last_break_position != static_cast<int>(line.last_char()))
    {
        text_line new_line(last_break_position, line.last_char());
        clear_cluster_widths(last_break_position, line.last_char());
        shape_text(new_line);
        add_line(new_line);
    }
}

void text_layout::add_line(text_line & line)
{
    if (lines_.empty())
    {
        line.set_first_line(true);
    }
    height_ += line.height();
    glyphs_count_ += line.size();
    width_ = std::max(width_, line.width());
    lines_.push_back(line);
}

void text_layout::clear_cluster_widths(unsigned first, unsigned last)
{
    for (unsigned i=first; i<last; ++i)
    {
        width_map_[i] = 0;
    }
}

void text_layout::clear()
{
    itemizer_.clear();
    lines_.clear();
    width_map_.clear();
    width_ = 0.0;
    height_ = 0.0;
    child_layout_list_.clear();
}

void text_layout::shape_text(text_line & line)
{
    harfbuzz_shaper::shape_text(line, itemizer_, width_map_, font_manager_, scale_factor_);
}

void text_layout::evaluate_properties(feature_impl const& feature, attributes const& attrs)
{
    // dx,dy
    double dx = util::apply_visitor(extract_value<value_double>(feature,attrs), properties_.dx);
    double dy = util::apply_visitor(extract_value<value_double>(feature,attrs), properties_.dy);
    displacement_ = properties_.displacement_evaluator_(dx,dy);

    wrap_width_ = util::apply_visitor(extract_value<value_double>(feature,attrs), properties_.wrap_width);

    double angle = util::apply_visitor(extract_value<value_double>(feature,attrs), properties_.orientation);
    orientation_.init(angle * M_PI/ 180.0);

    wrap_before_ = util::apply_visitor(extract_value<value_bool>(feature,attrs), properties_.wrap_before);
    rotate_displacement_ = util::apply_visitor(extract_value<value_bool>(feature,attrs), properties_.rotate_displacement);

    valign_ = util::apply_visitor(extract_value<vertical_alignment_enum>(feature,attrs),properties_.valign);
    halign_ = util::apply_visitor(extract_value<horizontal_alignment_enum>(feature,attrs),properties_.halign);
    jalign_ = util::apply_visitor(extract_value<justify_alignment_enum>(feature,attrs),properties_.jalign);

}

void text_layout::init_auto_alignment()
{
    if (valign_ == V_AUTO)
    {
        if (displacement_.y > 0.0) valign_ = V_BOTTOM;
        else if (displacement_.y < 0.0) valign_ = V_TOP;
        else valign_ = V_MIDDLE;
    }
    if (halign_ == H_AUTO)
    {
        if (displacement_.x > 0.0) halign_ = H_RIGHT;
        else if (displacement_.x < 0.0) halign_ = H_LEFT;
        else halign_ = H_MIDDLE;
    }
    if (jalign_ == J_AUTO)
    {
        if (displacement_.x > 0.0) jalign_ = J_LEFT;
        else if (displacement_.x < 0.0) jalign_ = J_RIGHT;
        else jalign_ = J_MIDDLE;
    }
}

pixel_position text_layout::alignment_offset() const
{
    pixel_position result(0,0);
    // if needed, adjust for desired vertical alignment
    if (valign_ == V_TOP)
    {
        result.y = -0.5 * height();  // move center up by 1/2 the total height
    }
    else if (valign_ == V_BOTTOM)
    {
        result.y = 0.5 * height();  // move center down by the 1/2 the total height
    }
    // set horizontal position to middle of text
    if (halign_ == H_LEFT)
    {
        result.x = -0.5 * width();  // move center left by 1/2 the string width
    }
    else if (halign_ == H_RIGHT)
    {
        result.x = 0.5 * width();  // move center right by 1/2 the string width
    }
    return result;
}

double text_layout::jalign_offset(double line_width) const
{
    if (jalign_ == J_MIDDLE) return -(line_width / 2.0);
    if (jalign_ == J_LEFT)   return -(width() / 2.0);
    if (jalign_ == J_RIGHT)  return (width() / 2.0) - line_width;
    return 0;
}

void layout_container::add(text_layout_ptr layout)
{
    text_ += layout->text();
    layouts_.push_back(layout);

    for (text_layout_ptr const& child_layout : layout->get_child_layouts())
    {
        add(child_layout);
    }
}

void layout_container::layout()
{
    bounds_.init(0,0,0,0);
    glyphs_count_ = 0;
    line_count_ = 0;

    bool first = true;
    for (text_layout_ptr const& layout : layouts_)
    {
        layout->layout();

        glyphs_count_ += layout->glyphs_count();
        line_count_ += layout->num_lines();

        if (first)
        {
            bounds_ = layout->bounds();
            first = false;
        }
        else
        {
            bounds_.expand_to_include(layout->bounds());
        }
    }
}

void layout_container::clear()
{
    layouts_.clear();
    text_.remove();
    bounds_.init(0,0,0,0);
    glyphs_count_ = 0;
    line_count_ = 0;
}

} //ns mapnik
<commit_msg>use icu shaper to avoid needing to build harfbuzz right now - refs #2396<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2013 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#include <mapnik/text/text_layout.hpp>
#include <mapnik/text/text_properties.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/debug.hpp>
#include <mapnik/text/icu_shaper.hpp>

// ICU
#include <unicode/brkiter.h>

namespace mapnik
{

// Output is centered around (0,0)
static void rotated_box2d(box2d<double> & box, rotation const& rot, pixel_position const& center, double width, double height)
{
    double half_width, half_height;
    if (rot.sin == 0 && rot.cos == 1.)
    {
        half_width  = width / 2.;
        half_height = height / 2.;
    }
    else
    {
        half_width  = (width * rot.cos + height * rot.sin) /2.;
        half_height = (width * rot.sin + height * rot.cos) /2.;
    }
    box.init(center.x - half_width, center.y - half_height, center.x + half_width, center.y + half_height);
}

pixel_position pixel_position::rotate(rotation const& rot) const
{
    return pixel_position(x * rot.cos - y * rot.sin, x * rot.sin + y * rot.cos);
}

text_layout::text_layout(face_manager_freetype & font_manager, double scale_factor, text_layout_properties const& properties)
    : font_manager_(font_manager),
      scale_factor_(scale_factor),
      itemizer_(),
      width_map_(),
      width_(0.0),
      height_(0.0),
      glyphs_count_(0),
      lines_(),
      properties_(properties) {}

void text_layout::add_text(mapnik::value_unicode_string const& str, evaluated_format_properties_ptr format)
{
    itemizer_.add_text(str, format);
}

void text_layout::add_child(text_layout_ptr const& child_layout)
{
    child_layout_list_.push_back(child_layout);
}

mapnik::value_unicode_string const& text_layout::text() const
{
    return itemizer_.text();
}

void text_layout::layout()
{
    unsigned num_lines = itemizer_.num_lines();
    for (unsigned i = 0; i < num_lines; ++i)
    {
        std::pair<unsigned, unsigned> line_limits = itemizer_.line(i);
        text_line line(line_limits.first, line_limits.second);
        //Break line if neccessary
        break_line(line, wrap_width_ * scale_factor_, text_ratio_, wrap_before_);
    }

    init_auto_alignment();

    // Find text origin.
    displacement_ = scale_factor_ * displacement_ + alignment_offset();
    if (rotate_displacement_) displacement_ = displacement_.rotate(!orientation_);
    // Find layout bounds, expanded for rotation
    rotated_box2d(bounds_, orientation_, displacement_, width_, height_);
}

// In the Unicode string characters are always stored in logical order.
// This makes line breaking easy. One word is added to the current line at a time. Once the line is too long
// we either go back one step or inset the line break at the current position (depending on "wrap_before" setting).
// At the end everything that is left over is added as the final line.
void text_layout::break_line(text_line & line, double wrap_width, unsigned text_ratio, bool wrap_before)
{
    shape_text(line);
    if (!wrap_width || line.width() < wrap_width)
    {
        add_line(line);
        return;

    }
    if (text_ratio)
    {
        double wrap_at;
        double string_width = line.width();
        double string_height = line.line_height();
        for (double i = 1.0; ((wrap_at = string_width/i)/(string_height*i)) > text_ratio && (string_width/i) > wrap_width; i += 1.0) ;
        wrap_width = wrap_at;
    }

    mapnik::value_unicode_string const& text = itemizer_.text();
    Locale locale; // TODO: Is the default constructor correct?
    UErrorCode status = U_ZERO_ERROR;
    BreakIterator *breakitr = BreakIterator::createLineInstance(locale, status);

    // Not breaking the text if an error occurs is probably the best thing we can do.
    // https://github.com/mapnik/mapnik/issues/2072
    if (!U_SUCCESS(status))
    {
        add_line(line);
        MAPNIK_LOG_ERROR(text_layout) << " could not create BreakIterator: " << u_errorName(status);
        return;
    }

    breakitr->setText(text);

    double current_line_length = 0;
    int last_break_position = static_cast<int>(line.first_char());
    for (unsigned i=line.first_char(); i < line.last_char(); ++i)
    {
        // TODO: character_spacing
        std::map<unsigned, double>::const_iterator width_itr = width_map_.find(i);
        if (width_itr != width_map_.end())
        {
            current_line_length += width_itr->second;
        }
        if (current_line_length <= wrap_width) continue;

        int break_position = wrap_before ? breakitr->preceding(i) : breakitr->following(i);
        // following() returns a break position after the last word. So DONE should only be returned
        // when calling preceding.
        if (break_position <= last_break_position || break_position == static_cast<int>(BreakIterator::DONE))
        {
            // A single word is longer than the maximum line width.
            // Violate line width requirement and choose next break position
            break_position = breakitr->following(i);
            if (break_position == static_cast<int>(BreakIterator::DONE))
            {
                break_position = line.last_char();
                MAPNIK_LOG_ERROR(text_layout) << "Unexpected result in break_line. Trying to recover...\n";
            }
        }
        // Break iterator operates on the whole string, while we only look at one line. So we need to
        // clamp break values.
        if (break_position < static_cast<int>(line.first_char()))
        {
            break_position = line.first_char();
        }
        if (break_position > static_cast<int>(line.last_char()))
        {
            break_position = line.last_char();
        }

        text_line new_line(last_break_position, break_position);
        clear_cluster_widths(last_break_position, break_position);
        shape_text(new_line);
        add_line(new_line);
        last_break_position = break_position;
        i = break_position - 1;
        current_line_length = 0;
    }
    if (last_break_position == static_cast<int>(line.first_char()))
    {
        // No line breaks => no reshaping required
        add_line(line);
    }
    else if (last_break_position != static_cast<int>(line.last_char()))
    {
        text_line new_line(last_break_position, line.last_char());
        clear_cluster_widths(last_break_position, line.last_char());
        shape_text(new_line);
        add_line(new_line);
    }
}

void text_layout::add_line(text_line & line)
{
    if (lines_.empty())
    {
        line.set_first_line(true);
    }
    height_ += line.height();
    glyphs_count_ += line.size();
    width_ = std::max(width_, line.width());
    lines_.push_back(line);
}

void text_layout::clear_cluster_widths(unsigned first, unsigned last)
{
    for (unsigned i=first; i<last; ++i)
    {
        width_map_[i] = 0;
    }
}

void text_layout::clear()
{
    itemizer_.clear();
    lines_.clear();
    width_map_.clear();
    width_ = 0.0;
    height_ = 0.0;
    child_layout_list_.clear();
}

void text_layout::shape_text(text_line & line)
{
    icu_shaper::shape_text(line, itemizer_, width_map_, font_manager_, scale_factor_);
}

void text_layout::evaluate_properties(feature_impl const& feature, attributes const& attrs)
{
    // dx,dy
    double dx = util::apply_visitor(extract_value<value_double>(feature,attrs), properties_.dx);
    double dy = util::apply_visitor(extract_value<value_double>(feature,attrs), properties_.dy);
    displacement_ = properties_.displacement_evaluator_(dx,dy);

    wrap_width_ = util::apply_visitor(extract_value<value_double>(feature,attrs), properties_.wrap_width);

    double angle = util::apply_visitor(extract_value<value_double>(feature,attrs), properties_.orientation);
    orientation_.init(angle * M_PI/ 180.0);

    wrap_before_ = util::apply_visitor(extract_value<value_bool>(feature,attrs), properties_.wrap_before);
    rotate_displacement_ = util::apply_visitor(extract_value<value_bool>(feature,attrs), properties_.rotate_displacement);

    valign_ = util::apply_visitor(extract_value<vertical_alignment_enum>(feature,attrs),properties_.valign);
    halign_ = util::apply_visitor(extract_value<horizontal_alignment_enum>(feature,attrs),properties_.halign);
    jalign_ = util::apply_visitor(extract_value<justify_alignment_enum>(feature,attrs),properties_.jalign);

}

void text_layout::init_auto_alignment()
{
    if (valign_ == V_AUTO)
    {
        if (displacement_.y > 0.0) valign_ = V_BOTTOM;
        else if (displacement_.y < 0.0) valign_ = V_TOP;
        else valign_ = V_MIDDLE;
    }
    if (halign_ == H_AUTO)
    {
        if (displacement_.x > 0.0) halign_ = H_RIGHT;
        else if (displacement_.x < 0.0) halign_ = H_LEFT;
        else halign_ = H_MIDDLE;
    }
    if (jalign_ == J_AUTO)
    {
        if (displacement_.x > 0.0) jalign_ = J_LEFT;
        else if (displacement_.x < 0.0) jalign_ = J_RIGHT;
        else jalign_ = J_MIDDLE;
    }
}

pixel_position text_layout::alignment_offset() const
{
    pixel_position result(0,0);
    // if needed, adjust for desired vertical alignment
    if (valign_ == V_TOP)
    {
        result.y = -0.5 * height();  // move center up by 1/2 the total height
    }
    else if (valign_ == V_BOTTOM)
    {
        result.y = 0.5 * height();  // move center down by the 1/2 the total height
    }
    // set horizontal position to middle of text
    if (halign_ == H_LEFT)
    {
        result.x = -0.5 * width();  // move center left by 1/2 the string width
    }
    else if (halign_ == H_RIGHT)
    {
        result.x = 0.5 * width();  // move center right by 1/2 the string width
    }
    return result;
}

double text_layout::jalign_offset(double line_width) const
{
    if (jalign_ == J_MIDDLE) return -(line_width / 2.0);
    if (jalign_ == J_LEFT)   return -(width() / 2.0);
    if (jalign_ == J_RIGHT)  return (width() / 2.0) - line_width;
    return 0;
}

void layout_container::add(text_layout_ptr layout)
{
    text_ += layout->text();
    layouts_.push_back(layout);

    for (text_layout_ptr const& child_layout : layout->get_child_layouts())
    {
        add(child_layout);
    }
}

void layout_container::layout()
{
    bounds_.init(0,0,0,0);
    glyphs_count_ = 0;
    line_count_ = 0;

    bool first = true;
    for (text_layout_ptr const& layout : layouts_)
    {
        layout->layout();

        glyphs_count_ += layout->glyphs_count();
        line_count_ += layout->num_lines();

        if (first)
        {
            bounds_ = layout->bounds();
            first = false;
        }
        else
        {
            bounds_.expand_to_include(layout->bounds());
        }
    }
}

void layout_container::clear()
{
    layouts_.clear();
    text_.remove();
    bounds_.init(0,0,0,0);
    glyphs_count_ = 0;
    line_count_ = 0;
}

} //ns mapnik
<|endoftext|>


<commit_before>/*
 *  soundpicker.cpp  -  widget to select a sound file or a beep
 *  Program:  kalarm
 *  Copyright (c) 2002-2006 by David Jarvie <software@astrojar.org.uk>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include "kalarm.h"

#include <QTimer>
#include <QLabel>
#include <QHBoxLayout>

#include <kglobal.h>
#include <klocale.h>
#include <kfiledialog.h>
#include <kstandarddirs.h>
#include <kiconloader.h>
#include <khbox.h>
#include <phonon/backendcapabilities.h>
#include <kdebug.h>

#include "combobox.h"
#include "functions.h"
#include "kalarmapp.h"
#include "pushbutton.h"
#include "sounddlg.h"
#include "soundpicker.moc"

// Collect these widget labels together to ensure consistent wording and
// translations across different modules.
QString SoundPicker::i18n_Sound()       { return i18nc("An audio sound", "Sound"); }
QString SoundPicker::i18n_None()        { return i18n("None"); }
QString SoundPicker::i18n_Beep()        { return i18n("Beep"); }
QString SoundPicker::i18n_Speak()       { return i18n("Speak"); }
QString SoundPicker::i18n_File()        { return i18n("Sound file"); }

SoundPicker::SoundPicker(QWidget* parent)
	: QFrame(parent),
	  mRevertType(false)
{
	QHBoxLayout* soundLayout = new QHBoxLayout(this);
	soundLayout->setMargin(0);
	soundLayout->setSpacing(2*KDialog::spacingHint());
	mTypeBox = new KHBox(this);    // this is to control the QWhatsThis text display area
	mTypeBox->setMargin(0);
	mTypeBox->setSpacing(KDialog::spacingHint());

	QLabel* label = new QLabel(i18nc("An audio sound", "&Sound:"), mTypeBox);
	label->setFixedSize(label->sizeHint());

	// Sound type combo box
	// The order of combo box entries must correspond with the 'Type' enum.
	mTypeCombo = new ComboBox(mTypeBox);
	mTypeCombo->addItem(i18n_None());     // index NONE
	mTypeCombo->addItem(i18n_Beep());     // index BEEP
	mTypeCombo->addItem(i18n_File());     // index PLAY_FILE
	mSpeakShowing = !theApp()->speechEnabled();
	showSpeak(!mSpeakShowing);            // index SPEAK (only displayed if appropriate)
	connect(mTypeCombo, SIGNAL(activated(int)), SLOT(slotTypeSelected(int)));
	label->setBuddy(mTypeCombo);
	soundLayout->addWidget(mTypeBox);

	// Sound file picker button
	mFilePicker = new PushButton(this);
	mFilePicker->setIcon(SmallIcon("playsound"));
	mFilePicker->setFixedSize(mFilePicker->sizeHint());
	connect(mFilePicker, SIGNAL(clicked()), SLOT(slotPickFile()));
	mFilePicker->setWhatsThis(i18n("Configure a sound file to play when the alarm is displayed."));
	soundLayout->addWidget(mFilePicker);

	// Initialise the file picker button state and tooltip
	mTypeCombo->setCurrentIndex(NONE);
	mFilePicker->setEnabled(false);
}

/******************************************************************************
* Set the read-only status of the widget.
*/
void SoundPicker::setReadOnly(bool readOnly)
{
	mTypeCombo->setReadOnly(readOnly);
	mFilePicker->setReadOnly(readOnly);
	mReadOnly = readOnly;
}

/******************************************************************************
* Show or hide the Speak option.
*/
void SoundPicker::showSpeak(bool show)
{
	if (!theApp()->speechEnabled())
		show = false;    // speech capability is not installed
	if (show == mSpeakShowing)
		return;    // no change
	QString whatsThis = "
" + i18n("Choose a sound to play when the message is displayed.")
	                  + "
" + i18n("%1: the message is displayed silently.", "" + i18n_None() + "")
	                  + "
" + i18n("%1: a simple beep is sounded.", "" + i18n_Beep() + "")
	                  + "
" + i18n("%1: an audio file is played. You will be prompted to choose the file and set play options.", "" + i18n_File() + "");
	if (!show  &&  mTypeCombo->currentIndex() == SPEAK)
		mTypeCombo->setCurrentIndex(NONE);
	mTypeCombo->removeItem(SPEAK);    // precaution in case of mix-ups
	if (show)
	{
		mTypeCombo->addItem(i18n_Speak());
		whatsThis += "
" + i18n("%1: the message text is spoken.", "" + i18n_Speak() + "") + "
";
	}
	mTypeBox->setWhatsThis(whatsThis + "
");
	mSpeakShowing = show;
}

/******************************************************************************
* Return the currently selected option.
*/
SoundPicker::Type SoundPicker::sound() const
{
	return static_cast<SoundPicker::Type>(mTypeCombo->currentIndex());
}

/******************************************************************************
* Return the selected sound file, if the File option is selected.
* Returns null string if File is not currently selected.
*/
QString SoundPicker::file() const
{
	return (mTypeCombo->currentIndex() == PLAY_FILE) ? mFile : QString();
}

/******************************************************************************
* Return the specified volumes (range 0 - 1).
* Returns < 0 if beep is currently selected, or if 'set volume' is not selected.
*/
float SoundPicker::volume(float& fadeVolume, int& fadeSeconds) const
{
	if (mTypeCombo->currentIndex() == PLAY_FILE  &&  !mFile.isEmpty())
	{
		fadeVolume  = mFadeVolume;
		fadeSeconds = mFadeSeconds;
		return mVolume;
	}
	else
	{
		fadeVolume  = -1;
		fadeSeconds = 0;
		return -1;
	}
}

/******************************************************************************
* Return whether sound file repetition is selected, if the main checkbox is checked.
* Returns false if beep is currently selected.
*/
bool SoundPicker::repeat() const
{
	return mTypeCombo->currentIndex() == PLAY_FILE  &&  !mFile.isEmpty()  &&  mRepeat;
}

/******************************************************************************
* Initialise the widget's state.
*/
void SoundPicker::set(SoundPicker::Type type, const QString& f, float volume, float fadeVolume, int fadeSeconds, bool repeat)
{
	if (type == PLAY_FILE  &&  f.isEmpty())
		type = BEEP;
	mFile        = f;
	mVolume      = volume;
	mFadeVolume  = fadeVolume;
	mFadeSeconds = fadeSeconds;
	mRepeat      = repeat;
	mFilePicker->setToolTip(mFile);
	mTypeCombo->setCurrentIndex(type);  // this doesn't trigger slotTypeSelected()
	mFilePicker->setEnabled(type == PLAY_FILE);
	mLastType = type;
}

/******************************************************************************
* Called when the sound option is changed.
*/
void SoundPicker::slotTypeSelected(int id)
{
	Type newType = static_cast<Type>(id);
	if (newType == mLastType  ||  mRevertType)
		return;
	if (mLastType == PLAY_FILE)
		mFilePicker->setEnabled(false);
	else if (newType == PLAY_FILE)
	{
		if (mFile.isEmpty())
		{
			slotPickFile();
			if (mFile.isEmpty())
				return;    // revert to previously selected type
		}
		mFilePicker->setEnabled(true);
	}
	mLastType = newType;
}

/******************************************************************************
* Called when the file picker button is clicked.
*/
void SoundPicker::slotPickFile()
{
	QString file = mFile;
	SoundDlg dlg(mFile, mVolume, mFadeVolume, mFadeSeconds, mRepeat, i18n("Sound File"), this);
	dlg.setReadOnly(mReadOnly);
	bool accepted = (dlg.exec() == QDialog::Accepted);
	if (mReadOnly)
		return;
	if (accepted)
	{
		float volume, fadeVolume;
		int   fadeTime;
		file         = dlg.getFile();
		mRepeat      = dlg.getSettings(volume, fadeVolume, fadeTime);
		mVolume      = volume;
		mFadeVolume  = fadeVolume;
		mFadeSeconds = fadeTime;
	}
	if (!file.isEmpty())
	{
		mFile       = file;
		mDefaultDir = dlg.defaultDir();
	}
	mFilePicker->setToolTip(mFile);
	if (mFile.isEmpty())
	{
		// No audio file is selected, so revert to previously selected option
#if 0
// Remove mRevertType, setLastType(), #include QTimer
		// But wait a moment until setting the radio button, or it won't work.
		mRevertType = true;   // prevent sound dialogue popping up twice
		QTimer::singleShot(0, this, SLOT(setLastType()));
#else
		mTypeCombo->setCurrentIndex(mLastType);
#endif
	}
}

/******************************************************************************
* Select the previously selected sound type.
*/
void SoundPicker::setLastType()
{
	mTypeCombo->setCurrentIndex(mLastType);
	mRevertType = false;
}

/******************************************************************************
* Display a dialogue to choose a sound file, initially highlighting any
* specified file. 'initialFile' must be a full path name or URL.
* 'defaultDir' is updated to the directory containing the chosen file.
* Reply = URL selected. If none is selected, URL.isEmpty() is true.
*/
QString SoundPicker::browseFile(QString& defaultDir, const QString& initialFile)
{
	static QString kdeSoundDir;     // directory containing KDE sound files
	if (defaultDir.isEmpty())
	{
		if (kdeSoundDir.isNull())
			kdeSoundDir = KGlobal::dirs()->findResourceDir("sound", "KDE_Notify.wav");
		defaultDir = kdeSoundDir;
	}
	QString filter = Phonon::BackendCapabilities::knownMimeTypes().join(" ");
	return KAlarm::browseFile(i18n("Choose Sound File"), defaultDir, initialFile, filter, KFile::ExistingOnly, 0);
}
<commit_msg>Prevent unnecessary terminal error message<commit_after>/*
 *  soundpicker.cpp  -  widget to select a sound file or a beep
 *  Program:  kalarm
 *  Copyright (c) 2002-2006 by David Jarvie <software@astrojar.org.uk>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include "kalarm.h"

#include <QTimer>
#include <QLabel>
#include <QHBoxLayout>

#include <kglobal.h>
#include <klocale.h>
#include <kfiledialog.h>
#include <kstandarddirs.h>
#include <kiconloader.h>
#include <khbox.h>
#include <phonon/backendcapabilities.h>
#include <kdebug.h>

#include "combobox.h"
#include "functions.h"
#include "kalarmapp.h"
#include "pushbutton.h"
#include "sounddlg.h"
#include "soundpicker.moc"

// Collect these widget labels together to ensure consistent wording and
// translations across different modules.
QString SoundPicker::i18n_Sound()       { return i18nc("An audio sound", "Sound"); }
QString SoundPicker::i18n_None()        { return i18n("None"); }
QString SoundPicker::i18n_Beep()        { return i18n("Beep"); }
QString SoundPicker::i18n_Speak()       { return i18n("Speak"); }
QString SoundPicker::i18n_File()        { return i18n("Sound file"); }

SoundPicker::SoundPicker(QWidget* parent)
	: QFrame(parent),
	  mRevertType(false)
{
	QHBoxLayout* soundLayout = new QHBoxLayout(this);
	soundLayout->setMargin(0);
	soundLayout->setSpacing(2*KDialog::spacingHint());
	mTypeBox = new KHBox(this);    // this is to control the QWhatsThis text display area
	mTypeBox->setMargin(0);
	mTypeBox->setSpacing(KDialog::spacingHint());

	QLabel* label = new QLabel(i18nc("An audio sound", "&Sound:"), mTypeBox);
	label->setFixedSize(label->sizeHint());

	// Sound type combo box
	// The order of combo box entries must correspond with the 'Type' enum.
	mTypeCombo = new ComboBox(mTypeBox);
	mTypeCombo->addItem(i18n_None());     // index NONE
	mTypeCombo->addItem(i18n_Beep());     // index BEEP
	mTypeCombo->addItem(i18n_File());     // index PLAY_FILE
	mSpeakShowing = !theApp()->speechEnabled();
	showSpeak(!mSpeakShowing);            // index SPEAK (only displayed if appropriate)
	connect(mTypeCombo, SIGNAL(activated(int)), SLOT(slotTypeSelected(int)));
	label->setBuddy(mTypeCombo);
	soundLayout->addWidget(mTypeBox);

	// Sound file picker button
	mFilePicker = new PushButton(this);
	mFilePicker->setIcon(SmallIcon("playsound"));
	mFilePicker->setFixedSize(mFilePicker->sizeHint());
	connect(mFilePicker, SIGNAL(clicked()), SLOT(slotPickFile()));
	mFilePicker->setWhatsThis(i18n("Configure a sound file to play when the alarm is displayed."));
	soundLayout->addWidget(mFilePicker);

	// Initialise the file picker button state and tooltip
	mTypeCombo->setCurrentIndex(NONE);
	mFilePicker->setEnabled(false);
}

/******************************************************************************
* Set the read-only status of the widget.
*/
void SoundPicker::setReadOnly(bool readOnly)
{
	mTypeCombo->setReadOnly(readOnly);
	mFilePicker->setReadOnly(readOnly);
	mReadOnly = readOnly;
}

/******************************************************************************
* Show or hide the Speak option.
*/
void SoundPicker::showSpeak(bool show)
{
	if (!theApp()->speechEnabled())
		show = false;    // speech capability is not installed
	if (show == mSpeakShowing)
		return;    // no change
	QString whatsThis = "
" + i18n("Choose a sound to play when the message is displayed.")
	                  + "
" + i18n("%1: the message is displayed silently.", "" + i18n_None() + "")
	                  + "
" + i18n("%1: a simple beep is sounded.", "" + i18n_Beep() + "")
	                  + "
" + i18n("%1: an audio file is played. You will be prompted to choose the file and set play options.", "" + i18n_File() + "");
	if (!show  &&  mTypeCombo->currentIndex() == SPEAK)
		mTypeCombo->setCurrentIndex(NONE);
	if (mTypeCombo->count() == SPEAK+1)
		mTypeCombo->removeItem(SPEAK);    // precaution in case of mix-ups
	if (show)
	{
		mTypeCombo->addItem(i18n_Speak());
		whatsThis += "
" + i18n("%1: the message text is spoken.", "" + i18n_Speak() + "") + "
";
	}
	mTypeBox->setWhatsThis(whatsThis + "
");
	mSpeakShowing = show;
}

/******************************************************************************
* Return the currently selected option.
*/
SoundPicker::Type SoundPicker::sound() const
{
	return static_cast<SoundPicker::Type>(mTypeCombo->currentIndex());
}

/******************************************************************************
* Return the selected sound file, if the File option is selected.
* Returns null string if File is not currently selected.
*/
QString SoundPicker::file() const
{
	return (mTypeCombo->currentIndex() == PLAY_FILE) ? mFile : QString();
}

/******************************************************************************
* Return the specified volumes (range 0 - 1).
* Returns < 0 if beep is currently selected, or if 'set volume' is not selected.
*/
float SoundPicker::volume(float& fadeVolume, int& fadeSeconds) const
{
	if (mTypeCombo->currentIndex() == PLAY_FILE  &&  !mFile.isEmpty())
	{
		fadeVolume  = mFadeVolume;
		fadeSeconds = mFadeSeconds;
		return mVolume;
	}
	else
	{
		fadeVolume  = -1;
		fadeSeconds = 0;
		return -1;
	}
}

/******************************************************************************
* Return whether sound file repetition is selected, if the main checkbox is checked.
* Returns false if beep is currently selected.
*/
bool SoundPicker::repeat() const
{
	return mTypeCombo->currentIndex() == PLAY_FILE  &&  !mFile.isEmpty()  &&  mRepeat;
}

/******************************************************************************
* Initialise the widget's state.
*/
void SoundPicker::set(SoundPicker::Type type, const QString& f, float volume, float fadeVolume, int fadeSeconds, bool repeat)
{
	if (type == PLAY_FILE  &&  f.isEmpty())
		type = BEEP;
	mFile        = f;
	mVolume      = volume;
	mFadeVolume  = fadeVolume;
	mFadeSeconds = fadeSeconds;
	mRepeat      = repeat;
	mFilePicker->setToolTip(mFile);
	mTypeCombo->setCurrentIndex(type);  // this doesn't trigger slotTypeSelected()
	mFilePicker->setEnabled(type == PLAY_FILE);
	mLastType = type;
}

/******************************************************************************
* Called when the sound option is changed.
*/
void SoundPicker::slotTypeSelected(int id)
{
	Type newType = static_cast<Type>(id);
	if (newType == mLastType  ||  mRevertType)
		return;
	if (mLastType == PLAY_FILE)
		mFilePicker->setEnabled(false);
	else if (newType == PLAY_FILE)
	{
		if (mFile.isEmpty())
		{
			slotPickFile();
			if (mFile.isEmpty())
				return;    // revert to previously selected type
		}
		mFilePicker->setEnabled(true);
	}
	mLastType = newType;
}

/******************************************************************************
* Called when the file picker button is clicked.
*/
void SoundPicker::slotPickFile()
{
	QString file = mFile;
	SoundDlg dlg(mFile, mVolume, mFadeVolume, mFadeSeconds, mRepeat, i18n("Sound File"), this);
	dlg.setReadOnly(mReadOnly);
	bool accepted = (dlg.exec() == QDialog::Accepted);
	if (mReadOnly)
		return;
	if (accepted)
	{
		float volume, fadeVolume;
		int   fadeTime;
		file         = dlg.getFile();
		mRepeat      = dlg.getSettings(volume, fadeVolume, fadeTime);
		mVolume      = volume;
		mFadeVolume  = fadeVolume;
		mFadeSeconds = fadeTime;
	}
	if (!file.isEmpty())
	{
		mFile       = file;
		mDefaultDir = dlg.defaultDir();
	}
	mFilePicker->setToolTip(mFile);
	if (mFile.isEmpty())
	{
		// No audio file is selected, so revert to previously selected option
#if 0
// Remove mRevertType, setLastType(), #include QTimer
		// But wait a moment until setting the radio button, or it won't work.
		mRevertType = true;   // prevent sound dialogue popping up twice
		QTimer::singleShot(0, this, SLOT(setLastType()));
#else
		mTypeCombo->setCurrentIndex(mLastType);
#endif
	}
}

/******************************************************************************
* Select the previously selected sound type.
*/
void SoundPicker::setLastType()
{
	mTypeCombo->setCurrentIndex(mLastType);
	mRevertType = false;
}

/******************************************************************************
* Display a dialogue to choose a sound file, initially highlighting any
* specified file. 'initialFile' must be a full path name or URL.
* 'defaultDir' is updated to the directory containing the chosen file.
* Reply = URL selected. If none is selected, URL.isEmpty() is true.
*/
QString SoundPicker::browseFile(QString& defaultDir, const QString& initialFile)
{
	static QString kdeSoundDir;     // directory containing KDE sound files
	if (defaultDir.isEmpty())
	{
		if (kdeSoundDir.isNull())
			kdeSoundDir = KGlobal::dirs()->findResourceDir("sound", "KDE_Notify.wav");
		defaultDir = kdeSoundDir;
	}
	QString filter = Phonon::BackendCapabilities::knownMimeTypes().join(" ");
	return KAlarm::browseFile(i18n("Choose Sound File"), defaultDir, initialFile, filter, KFile::ExistingOnly, 0);
}
<|endoftext|>