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

#include "votca/xtp/gaussianwriter.h"
#include "votca/xtp/basisset.h"
#include <boost/algorithm/string.hpp>
#include <boost/format.hpp>
#include <sstream>
#include <votca/tools/eigenio_matrixmarket.h>

namespace votca {
namespace xtp {

/*
 * This function converts VOTCA's L enum to the gaussian equivalent.
 * Gaussian uses a minus sign to indicate spherical shells, but -1 for sp.
 */
Index GaussianWriter::toGaussianL(L l) const {
  switch (l) {
    case L::S:
      return 0;
    case L::P:
      return 1;
    default:
      return -1 * static_cast<Index>(l);
  }
}

std::string GaussianWriter::reorderedMOCoefficients(
    const Orbitals& orbitals) const {
  OrbReorder reorder(gaussianOrder, gaussianMultipliers, true);
  Eigen::MatrixXd moCoefficients = orbitals.MOs().eigenvectors();
  reorder.reorderOrbitals(moCoefficients, orbitals.SetupDftBasis());

  // put the reordered mos in a string
  std::stringstream mos_string;

  int temp_int = 1;
  for (Index i = 0; i < moCoefficients.rows(); ++i) {
    for (Index j = 0; j < moCoefficients.cols(); ++j) {
      mos_string << boost::format("%16.8e") % moCoefficients(j, i);
      if (temp_int % 5 == 0) {
        mos_string << "\n";
      }
      temp_int++;
    }
  }
  mos_string << ((temp_int - 1) % 5 == 0 ? "" : "\n");

  return mos_string.str();
}

std::string GaussianWriter::densityMatrixToString(
    const Orbitals& orbitals) const {
  OrbReorder reorder(gaussianOrder, gaussianMultipliers, true);
  Eigen::MatrixXd density = orbitals.DensityMatrixGroundState();
  reorder.reorderOperator(density, orbitals.SetupDftBasis());

  // put the reordered mos in a string
  std::stringstream density_string;

  int temp_int = 1;

  for (Index i = 0; i < density.rows(); ++i) {
    for (Index j = 0; j <= i; ++j) {
      density_string << boost::format("%16.8e") % density(i, j);
      if (temp_int % 5 == 0) {
        density_string << "\n";
      }
      temp_int++;
    }
  }
  density_string << ((temp_int - 1) % 5 == 0 ? "" : "\n");

  return density_string.str();
}

void GaussianWriter::WriteFile(const std::string& basename,
                               const Orbitals& orbitals) const {
  if (!orbitals.hasDFTbasisName()) {
    throw std::runtime_error(".orb file does not contain a basisset name");
  }

  AOBasis basis = orbitals.SetupDftBasis();

  std::ofstream outFile(basename + ".fchk");

  if (outFile.is_open()) {
    XTP_LOG(Log::error, _log)
      << "Start writing to " << (basename + ".fchk") << std::flush;
    int temp_int;
    // job description
    outFile << basename << ", fchk created by VOTCA-XTP\n";
    outFile << "SP    RHF    " << orbitals.getDFTbasisName()
            << "\n";

    // clang-format off
    outFile << boost::format("%-43s%-2s%15d\n") % "Number of atoms" % "I" % orbitals.QMAtoms().size();
    outFile << boost::format("%-43s%-2s%15d\n") % "Charge" % "I" % 0;
    outFile << boost::format("%-43s%-2s%15d\n") % "Multiplicity" % "I" % 1;
    outFile << boost::format("%-43s%-2s%15d\n") % "Number of electrons" % "I" % (2*orbitals.getNumberOfAlphaElectrons());
    outFile << boost::format("%-43s%-2s%15d\n") % "Number of alpha electrons" % "I" % orbitals.getNumberOfAlphaElectrons();
    outFile << boost::format("%-43s%-2s%15d\n") % "Number of beta electrons" % "I" % orbitals.getNumberOfAlphaElectrons();
    outFile << boost::format("%-43s%-2s%15d\n") % "Number of basis functions" % "I" % basis.AOBasisSize();
    outFile << boost::format("%-43s%-2s%15d\n") % "Number of independent functions" % "I" % basis.AOBasisSize();
    // clang-format on
    // ATOMIC NUMBERS
    outFile << boost::format("%-43s%-2s N=  %10d\n") % "Atomic numbers" % "I" %
                   orbitals.QMAtoms().size();
    temp_int = 1;
    for (const auto& atom : orbitals.QMAtoms()) {
      outFile << boost::format("%12d") % atom.getElementNumber();
      if (temp_int % 6 == 0) {
        outFile << "\n";
      }
      temp_int++;
    }
    outFile << ((temp_int - 1) % 6 == 0 ? "" : "\n");
    // NUCLEAR CHARGES
    outFile << boost::format("%-43s%-2s N=  %10d\n") % "Nuclear charges" % "R" %
                   orbitals.QMAtoms().size();
    temp_int = 1;
    for (const auto& atom : orbitals.QMAtoms()) {
      outFile << boost::format("%16.8e") % (double)atom.getNuccharge();
      if (temp_int % 5 == 0) {
        outFile << "\n";
      }
      temp_int++;
    }
    outFile << ((temp_int - 1) % 5 == 0 ? "" : "\n");
    // CURRENT CARTESIAN COORDINATES
    outFile << boost::format("%-43s%-2s N=  %10d\n") %
                   "Current cartesian coordinates" % "R" %
                   (3 * orbitals.QMAtoms().size());
    temp_int = 1;
    for (const auto& atom : orbitals.QMAtoms()) {
      for (int i = 0; i < 3; ++i) {
        outFile << boost::format("%16.8e") % atom.getPos()(i);
        if (temp_int % 5 == 0) {
          outFile << "\n";
        }
        temp_int++;
      }
    }
    outFile << ((temp_int - 1) % 5 == 0 ? "" : "\n");
    // NUMBER OF PRIMITIVE SHELLS
    outFile << boost::format("%-43s%-2s%15d\n") % "Number of primitive shells" %
                   "I" % basis.getNumberOfPrimitives();
    // NUMBER OF CONTRACTED SHELLS
    outFile << boost::format("%-43s%-2s%15d\n") %
                   "Number of contracted shells" % "I" % basis.getNumofShells();
    // PURE/CARTESIAN D
    outFile << boost::format("%-43s%-2s%15d\n") % "Pure/Cartesian d shells " %
                   "I" % 0;
    // PURE/CARTESIAN F
    outFile << boost::format("%-43s%-2s%15d\n") % "Pure/Cartesian f shells " %
                   "I" % 0;
    // HIGHEST ANGULAR MOMENTUM
    outFile << boost::format("%-43s%-2s%15d\n") % "Highest angular momentum " %
                   "I" % basis.getMaxL();
    // HIGHEST ANGULAR MOMENTUM
    outFile << boost::format("%-43s%-2s%15d\n") %
                   "Largest degree of contraction " % "I" % basis.getMaxNprim();
    // SHELL TYPES
    outFile << boost::format("%-43s%-2s N=  %10d\n") % "Shell types" % "I" %
                   (basis.getNumofShells());
    temp_int = 1;
    for (const auto& shell : basis) {
      outFile << boost::format("%12d") % toGaussianL(shell.getL());
      if (temp_int % 6 == 0) {
        outFile << "\n";
      }
      temp_int++;
    }
    outFile << ((temp_int - 1) % 6 == 0 ? "" : "\n");
    // NR OF PRIMITIVES PER SHELL
    outFile << boost::format("%-43s%-2s N=  %10d\n") %
                   "Number of primitives per shell" % "I" %
                   (basis.getNumofShells());
    temp_int = 1;
    for (const AOShell& shell : basis) {
      outFile << boost::format("%12d") % shell.getSize();
      if (temp_int % 6 == 0) {
        outFile << "\n";
      }
      temp_int++;
    }
    outFile << ((temp_int - 1) % 6 == 0 ? "" : "\n");
    // SHELL TO ATOM MAP
    outFile << boost::format("%-43s%-2s N=  %10d\n") % "Shell to atom map" %
                   "I" % (basis.getNumofShells());
    temp_int = 1;
    for (const AOShell& shell : basis) {
      // Gaussian indices start at 1, hence the + 1
      outFile << boost::format("%12d") % (shell.getAtomIndex() + 1);
      if (temp_int % 6 == 0) {
        outFile << "\n";
      }
      temp_int++;
    }
    outFile << ((temp_int - 1) % 6 == 0 ? "" : "\n");
    // PRIMITIVE EXPONENTS
    outFile << boost::format("%-43s%-2s N=  %10d\n") % "Primitive exponents" %
                   "R" % basis.getNumberOfPrimitives();
    temp_int = 1;
    for (const AOShell& shell : basis) {
      for (const AOGaussianPrimitive& prim : shell) {
        outFile << boost::format("%16.8e") % prim.getDecay();
        if (temp_int % 5 == 0) {
          outFile << "\n";
        }
        temp_int++;
      }
    }
    outFile << ((temp_int - 1) % 5 == 0 ? "" : "\n");
    // CONTRACTION COEFFICIENTS
    outFile << boost::format("%-43s%-2s N=  %10d\n") %
                   "Contraction coefficients" % "R" %
                   basis.getNumberOfPrimitives();
    temp_int = 1;
    for (const AOShell& shell : basis) {
      for (const AOGaussianPrimitive& prim : shell) {
        outFile << boost::format("%16.8e") % prim.getContraction();
        if (temp_int % 5 == 0) {
          outFile << "\n";
        }
        temp_int++;
      }
    }
    outFile << ((temp_int - 1) % 5 == 0 ? "" : "\n");
    // SHELL COORDINATES
    outFile << boost::format("%-43s%-2s N=  %10d\n") %
                   "Coordinates of each shell" % "R" %
                   (3 * basis.getNumofShells());
    temp_int = 1;
    for (const AOShell& shell : basis) {
      for (int i = 0; i < 3; ++i) {
        outFile << boost::format("%16.8e") % shell.getPos()(i);
        if (temp_int % 5 == 0) {
          outFile << "\n";
        }
        temp_int++;
      }
    }
    outFile << ((temp_int - 1) % 5 == 0 ? "" : "\n");
    // TOTAL ENERGY
    outFile << boost::format("%-43s%-2s%22.15e\n") % "Total Energy" % "R" %
                   orbitals.getDFTTotalEnergy();
    // ALPHA ORBITAL ENERGIES
    outFile << boost::format("%-43s%-2s N=  %10d\n") %
                   "Alpha Orbital Energies" % "R" %
                   orbitals.MOs().eigenvalues().size();
    temp_int = 1;
    for (Index i = 0; i < orbitals.MOs().eigenvalues().size(); ++i) {
      outFile << boost::format("%16.8e") % orbitals.MOs().eigenvalues()[i];
      if (temp_int % 5 == 0) {
        outFile << "\n";
      }
      temp_int++;
    }
    outFile << ((temp_int - 1) % 5 == 0 ? "" : "\n");
    // ALPHA ORBITAL ENERGIES
    outFile << boost::format("%-43s%-2s N=  %10d\n") % "Alpha MO coefficients" %
                   "R" %
                   (orbitals.MOs().eigenvalues().size() *
                    orbitals.MOs().eigenvalues().size());
    outFile << reorderedMOCoefficients(orbitals);
    // DENSITY MATRIX
    outFile << boost::format("%-43s%-2s N=  %10d\n") % "Total SCF Density" %
                   "R" %
                   ((orbitals.MOs().eigenvalues().size() *
                     (orbitals.MOs().eigenvalues().size() - 1)) /
                        2 +
                    orbitals.MOs().eigenvalues().size());
    outFile << densityMatrixToString(orbitals);
    XTP_LOG(Log::error, _log) << "Done writing \n" << std::flush;
  }
}

}  // namespace xtp
}  // namespace votca<commit_msg>Format code using clang-format version 11.0.0 (Fedora 11.0.0-2.fc33)<commit_after>/*
 *            Copyright 2009-2021 The VOTCA Development Team
 *                       (http://www.votca.org)
 *
 *      Licensed under the Apache License, Version 2.0 (the "License")
 *
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *              http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include "votca/xtp/gaussianwriter.h"
#include "votca/xtp/basisset.h"
#include <boost/algorithm/string.hpp>
#include <boost/format.hpp>
#include <sstream>
#include <votca/tools/eigenio_matrixmarket.h>

namespace votca {
namespace xtp {

/*
 * This function converts VOTCA's L enum to the gaussian equivalent.
 * Gaussian uses a minus sign to indicate spherical shells, but -1 for sp.
 */
Index GaussianWriter::toGaussianL(L l) const {
  switch (l) {
    case L::S:
      return 0;
    case L::P:
      return 1;
    default:
      return -1 * static_cast<Index>(l);
  }
}

std::string GaussianWriter::reorderedMOCoefficients(
    const Orbitals& orbitals) const {
  OrbReorder reorder(gaussianOrder, gaussianMultipliers, true);
  Eigen::MatrixXd moCoefficients = orbitals.MOs().eigenvectors();
  reorder.reorderOrbitals(moCoefficients, orbitals.SetupDftBasis());

  // put the reordered mos in a string
  std::stringstream mos_string;

  int temp_int = 1;
  for (Index i = 0; i < moCoefficients.rows(); ++i) {
    for (Index j = 0; j < moCoefficients.cols(); ++j) {
      mos_string << boost::format("%16.8e") % moCoefficients(j, i);
      if (temp_int % 5 == 0) {
        mos_string << "\n";
      }
      temp_int++;
    }
  }
  mos_string << ((temp_int - 1) % 5 == 0 ? "" : "\n");

  return mos_string.str();
}

std::string GaussianWriter::densityMatrixToString(
    const Orbitals& orbitals) const {
  OrbReorder reorder(gaussianOrder, gaussianMultipliers, true);
  Eigen::MatrixXd density = orbitals.DensityMatrixGroundState();
  reorder.reorderOperator(density, orbitals.SetupDftBasis());

  // put the reordered mos in a string
  std::stringstream density_string;

  int temp_int = 1;

  for (Index i = 0; i < density.rows(); ++i) {
    for (Index j = 0; j <= i; ++j) {
      density_string << boost::format("%16.8e") % density(i, j);
      if (temp_int % 5 == 0) {
        density_string << "\n";
      }
      temp_int++;
    }
  }
  density_string << ((temp_int - 1) % 5 == 0 ? "" : "\n");

  return density_string.str();
}

void GaussianWriter::WriteFile(const std::string& basename,
                               const Orbitals& orbitals) const {
  if (!orbitals.hasDFTbasisName()) {
    throw std::runtime_error(".orb file does not contain a basisset name");
  }

  AOBasis basis = orbitals.SetupDftBasis();

  std::ofstream outFile(basename + ".fchk");

  if (outFile.is_open()) {
    XTP_LOG(Log::error, _log)
        << "Start writing to " << (basename + ".fchk") << std::flush;
    int temp_int;
    // job description
    outFile << basename << ", fchk created by VOTCA-XTP\n";
    outFile << "SP    RHF    " << orbitals.getDFTbasisName() << "\n";

    // clang-format off
    outFile << boost::format("%-43s%-2s%15d\n") % "Number of atoms" % "I" % orbitals.QMAtoms().size();
    outFile << boost::format("%-43s%-2s%15d\n") % "Charge" % "I" % 0;
    outFile << boost::format("%-43s%-2s%15d\n") % "Multiplicity" % "I" % 1;
    outFile << boost::format("%-43s%-2s%15d\n") % "Number of electrons" % "I" % (2*orbitals.getNumberOfAlphaElectrons());
    outFile << boost::format("%-43s%-2s%15d\n") % "Number of alpha electrons" % "I" % orbitals.getNumberOfAlphaElectrons();
    outFile << boost::format("%-43s%-2s%15d\n") % "Number of beta electrons" % "I" % orbitals.getNumberOfAlphaElectrons();
    outFile << boost::format("%-43s%-2s%15d\n") % "Number of basis functions" % "I" % basis.AOBasisSize();
    outFile << boost::format("%-43s%-2s%15d\n") % "Number of independent functions" % "I" % basis.AOBasisSize();
    // clang-format on
    // ATOMIC NUMBERS
    outFile << boost::format("%-43s%-2s N=  %10d\n") % "Atomic numbers" % "I" %
                   orbitals.QMAtoms().size();
    temp_int = 1;
    for (const auto& atom : orbitals.QMAtoms()) {
      outFile << boost::format("%12d") % atom.getElementNumber();
      if (temp_int % 6 == 0) {
        outFile << "\n";
      }
      temp_int++;
    }
    outFile << ((temp_int - 1) % 6 == 0 ? "" : "\n");
    // NUCLEAR CHARGES
    outFile << boost::format("%-43s%-2s N=  %10d\n") % "Nuclear charges" % "R" %
                   orbitals.QMAtoms().size();
    temp_int = 1;
    for (const auto& atom : orbitals.QMAtoms()) {
      outFile << boost::format("%16.8e") % (double)atom.getNuccharge();
      if (temp_int % 5 == 0) {
        outFile << "\n";
      }
      temp_int++;
    }
    outFile << ((temp_int - 1) % 5 == 0 ? "" : "\n");
    // CURRENT CARTESIAN COORDINATES
    outFile << boost::format("%-43s%-2s N=  %10d\n") %
                   "Current cartesian coordinates" % "R" %
                   (3 * orbitals.QMAtoms().size());
    temp_int = 1;
    for (const auto& atom : orbitals.QMAtoms()) {
      for (int i = 0; i < 3; ++i) {
        outFile << boost::format("%16.8e") % atom.getPos()(i);
        if (temp_int % 5 == 0) {
          outFile << "\n";
        }
        temp_int++;
      }
    }
    outFile << ((temp_int - 1) % 5 == 0 ? "" : "\n");
    // NUMBER OF PRIMITIVE SHELLS
    outFile << boost::format("%-43s%-2s%15d\n") % "Number of primitive shells" %
                   "I" % basis.getNumberOfPrimitives();
    // NUMBER OF CONTRACTED SHELLS
    outFile << boost::format("%-43s%-2s%15d\n") %
                   "Number of contracted shells" % "I" % basis.getNumofShells();
    // PURE/CARTESIAN D
    outFile << boost::format("%-43s%-2s%15d\n") % "Pure/Cartesian d shells " %
                   "I" % 0;
    // PURE/CARTESIAN F
    outFile << boost::format("%-43s%-2s%15d\n") % "Pure/Cartesian f shells " %
                   "I" % 0;
    // HIGHEST ANGULAR MOMENTUM
    outFile << boost::format("%-43s%-2s%15d\n") % "Highest angular momentum " %
                   "I" % basis.getMaxL();
    // HIGHEST ANGULAR MOMENTUM
    outFile << boost::format("%-43s%-2s%15d\n") %
                   "Largest degree of contraction " % "I" % basis.getMaxNprim();
    // SHELL TYPES
    outFile << boost::format("%-43s%-2s N=  %10d\n") % "Shell types" % "I" %
                   (basis.getNumofShells());
    temp_int = 1;
    for (const auto& shell : basis) {
      outFile << boost::format("%12d") % toGaussianL(shell.getL());
      if (temp_int % 6 == 0) {
        outFile << "\n";
      }
      temp_int++;
    }
    outFile << ((temp_int - 1) % 6 == 0 ? "" : "\n");
    // NR OF PRIMITIVES PER SHELL
    outFile << boost::format("%-43s%-2s N=  %10d\n") %
                   "Number of primitives per shell" % "I" %
                   (basis.getNumofShells());
    temp_int = 1;
    for (const AOShell& shell : basis) {
      outFile << boost::format("%12d") % shell.getSize();
      if (temp_int % 6 == 0) {
        outFile << "\n";
      }
      temp_int++;
    }
    outFile << ((temp_int - 1) % 6 == 0 ? "" : "\n");
    // SHELL TO ATOM MAP
    outFile << boost::format("%-43s%-2s N=  %10d\n") % "Shell to atom map" %
                   "I" % (basis.getNumofShells());
    temp_int = 1;
    for (const AOShell& shell : basis) {
      // Gaussian indices start at 1, hence the + 1
      outFile << boost::format("%12d") % (shell.getAtomIndex() + 1);
      if (temp_int % 6 == 0) {
        outFile << "\n";
      }
      temp_int++;
    }
    outFile << ((temp_int - 1) % 6 == 0 ? "" : "\n");
    // PRIMITIVE EXPONENTS
    outFile << boost::format("%-43s%-2s N=  %10d\n") % "Primitive exponents" %
                   "R" % basis.getNumberOfPrimitives();
    temp_int = 1;
    for (const AOShell& shell : basis) {
      for (const AOGaussianPrimitive& prim : shell) {
        outFile << boost::format("%16.8e") % prim.getDecay();
        if (temp_int % 5 == 0) {
          outFile << "\n";
        }
        temp_int++;
      }
    }
    outFile << ((temp_int - 1) % 5 == 0 ? "" : "\n");
    // CONTRACTION COEFFICIENTS
    outFile << boost::format("%-43s%-2s N=  %10d\n") %
                   "Contraction coefficients" % "R" %
                   basis.getNumberOfPrimitives();
    temp_int = 1;
    for (const AOShell& shell : basis) {
      for (const AOGaussianPrimitive& prim : shell) {
        outFile << boost::format("%16.8e") % prim.getContraction();
        if (temp_int % 5 == 0) {
          outFile << "\n";
        }
        temp_int++;
      }
    }
    outFile << ((temp_int - 1) % 5 == 0 ? "" : "\n");
    // SHELL COORDINATES
    outFile << boost::format("%-43s%-2s N=  %10d\n") %
                   "Coordinates of each shell" % "R" %
                   (3 * basis.getNumofShells());
    temp_int = 1;
    for (const AOShell& shell : basis) {
      for (int i = 0; i < 3; ++i) {
        outFile << boost::format("%16.8e") % shell.getPos()(i);
        if (temp_int % 5 == 0) {
          outFile << "\n";
        }
        temp_int++;
      }
    }
    outFile << ((temp_int - 1) % 5 == 0 ? "" : "\n");
    // TOTAL ENERGY
    outFile << boost::format("%-43s%-2s%22.15e\n") % "Total Energy" % "R" %
                   orbitals.getDFTTotalEnergy();
    // ALPHA ORBITAL ENERGIES
    outFile << boost::format("%-43s%-2s N=  %10d\n") %
                   "Alpha Orbital Energies" % "R" %
                   orbitals.MOs().eigenvalues().size();
    temp_int = 1;
    for (Index i = 0; i < orbitals.MOs().eigenvalues().size(); ++i) {
      outFile << boost::format("%16.8e") % orbitals.MOs().eigenvalues()[i];
      if (temp_int % 5 == 0) {
        outFile << "\n";
      }
      temp_int++;
    }
    outFile << ((temp_int - 1) % 5 == 0 ? "" : "\n");
    // ALPHA ORBITAL ENERGIES
    outFile << boost::format("%-43s%-2s N=  %10d\n") % "Alpha MO coefficients" %
                   "R" %
                   (orbitals.MOs().eigenvalues().size() *
                    orbitals.MOs().eigenvalues().size());
    outFile << reorderedMOCoefficients(orbitals);
    // DENSITY MATRIX
    outFile << boost::format("%-43s%-2s N=  %10d\n") % "Total SCF Density" %
                   "R" %
                   ((orbitals.MOs().eigenvalues().size() *
                     (orbitals.MOs().eigenvalues().size() - 1)) /
                        2 +
                    orbitals.MOs().eigenvalues().size());
    outFile << densityMatrixToString(orbitals);
    XTP_LOG(Log::error, _log) << "Done writing \n" << std::flush;
  }
}

}  // namespace xtp
}  // namespace votca<|endoftext|>


<commit_before>//=============================================================================================================
/**
 * @file     test_mne_project_to_surface.cpp
 * @author   Ruben Dörfel <doerfelruben@aol.com>
 * @since    0.1.6
 * @date     August, 2020
 *
 * @section  LICENSE
 *
 * Copyright (C) 2020, Ruben Dörfel. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that
 * the following conditions are met:
 *     * Redistributions of source code must retain the above copyright notice, this list of conditions and the
 *       following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
 *       the following disclaimer in the documentation and/or other materials provided with the distribution.
 *     * Neither the name of MNE-CPP authors nor the names of its contributors may be used
 *       to endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * @brief     Test the MNEProjectToSurface class..
 *
 */

//=============================================================================================================
// INCLUDES
//=============================================================================================================

#include <utils/generics/applicationlogger.h>
#include <mne/mne_project_to_surface.h>
#include <mne/mne_bem.h>
#include <mne/mne_bem_surface.h>
#include <utils/ioutils.h>

//=============================================================================================================
// QT INCLUDES
//=============================================================================================================

#include <QtCore/QCoreApplication>
#include <QtTest>

//=============================================================================================================
// Eigen
//=============================================================================================================

#include <Eigen/Dense>

//=============================================================================================================
// Using NAMESPACE
//=============================================================================================================

using namespace MNELIB;
using namespace Eigen;

//=============================================================================================================
/**
 * DECLARE CLASS TestMNEProjectToSurface
 *
 * @brief The TestMNEProjectToSurface class provides read write read fiff verification tests
 *
 */
class TestMNEProjectToSurface: public QObject
{
    Q_OBJECT

public:
    TestMNEProjectToSurface();

private slots:
    void initTestCase();
    void compareValue();
    // add other compareFunctions here
    void cleanupTestCase();

private:
    // declare your thresholds, variables and error values here
    double dEpsilon;
    MatrixXf matResult;
    MatrixXd matRef;

};

//=============================================================================================================

TestMNEProjectToSurface::TestMNEProjectToSurface()
    : dEpsilon(0.000001)
{
}

//=============================================================================================================

void TestMNEProjectToSurface::initTestCase()
{
    QFile t_fileBem(QCoreApplication::applicationDirPath() + "/mne-cpp-test-data/subjects/sample/bem/sample-1280-1280-1280-bem.fif");
    QString sRef(QCoreApplication::applicationDirPath() + "/mne-cpp-test-data/Result/mne_project_to_surface.txt");

    MNEBem bemHead(t_fileBem);
    MNEBemSurface::SPtr bemSurface = MNEBemSurface::SPtr::create(bemHead[0]);
    MNEProjectToSurface::SPtr mneSurfacePoints = MNEProjectToSurface::SPtr::create(*bemSurface);

    VectorXi vecNearest;                        // Triangle of the new point
    VectorXf vecDist;                           // The Distance between matX and matP

    MatrixXf matPointsShifted = bemSurface->rr.cast<float>() * 1.1;         // Move all points with same amout from surface
    int iNP = matPointsShifted.rows();

    mneSurfacePoints->mne_find_closest_on_surface(matPointsShifted, iNP, matResult, vecNearest, vecDist);

    // read reference
    // UTILSLIB::IOUtils::write_eigen_matrix(matResult,sRef);
    UTILSLIB::IOUtils::read_eigen_matrix(matRef,sRef);
}

//=============================================================================================================

void TestMNEProjectToSurface::compareValue()
{
    // check if MNEProjectToSurface was able to get original points on surface
    MatrixXf matDiff = matRef.cast<float>() - matResult;
    qDebug() << "Summed Difference: " << std::abs(matDiff.sum());
    QVERIFY(std::abs(matDiff.sum()) < dEpsilon);
}

//=============================================================================================================

void TestMNEProjectToSurface::cleanupTestCase()
{
}

//=============================================================================================================
// MAIN
//=============================================================================================================

QTEST_GUILESS_MAIN(TestMNEProjectToSurface)
#include "test_mne_project_to_surface.moc"

<commit_msg>Maint: apply change requests<commit_after>//=============================================================================================================
/**
 * @file     test_mne_project_to_surface.cpp
 * @author   Ruben Dörfel <doerfelruben@aol.com>
 * @since    0.1.6
 * @date     August, 2020
 *
 * @section  LICENSE
 *
 * Copyright (C) 2020, Ruben Dörfel. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that
 * the following conditions are met:
 *     * Redistributions of source code must retain the above copyright notice, this list of conditions and the
 *       following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
 *       the following disclaimer in the documentation and/or other materials provided with the distribution.
 *     * Neither the name of MNE-CPP authors nor the names of its contributors may be used
 *       to endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * @brief     Test the MNEProjectToSurface class.
 *
 */

//=============================================================================================================
// INCLUDES
//=============================================================================================================

#include <utils/generics/applicationlogger.h>
#include <mne/mne_project_to_surface.h>
#include <mne/mne_bem.h>
#include <mne/mne_bem_surface.h>
#include <utils/ioutils.h>

//=============================================================================================================
// QT INCLUDES
//=============================================================================================================

#include <QtCore/QCoreApplication>
#include <QtTest>

//=============================================================================================================
// Eigen
//=============================================================================================================

#include <Eigen/Dense>

//=============================================================================================================
// Using NAMESPACE
//=============================================================================================================

using namespace MNELIB;
using namespace Eigen;

//=============================================================================================================
/**
 * DECLARE CLASS TestMNEProjectToSurface
 *
 * @brief The TestMNEProjectToSurface class provides MNEProjectToSurface verification tests
 *
 */
class TestMNEProjectToSurface: public QObject
{
    Q_OBJECT

public:
    TestMNEProjectToSurface();

private slots:
    void initTestCase();
    void compareValue();
    void cleanupTestCase();

private:
    // declare thresholds, and variables
    double dEpsilon;
    MatrixXf matResult;
    MatrixXd matRef;

};

//=============================================================================================================

TestMNEProjectToSurface::TestMNEProjectToSurface()
    : dEpsilon(0.000001)
{
}

//=============================================================================================================

void TestMNEProjectToSurface::initTestCase()
{
    QFile t_fileBem(QCoreApplication::applicationDirPath() + "/mne-cpp-test-data/subjects/sample/bem/sample-1280-1280-1280-bem.fif");
    QString sRef(QCoreApplication::applicationDirPath() + "/mne-cpp-test-data/Result/mne_project_to_surface.txt");

    MNEBem bemHead(t_fileBem);
    MNEBemSurface::SPtr bemSurface = MNEBemSurface::SPtr::create(bemHead[0]);
    MNEProjectToSurface::SPtr mneSurfacePoints = MNEProjectToSurface::SPtr::create(*bemSurface);

    VectorXi vecNearest;    // Triangle of the new point
    VectorXf vecDist;       // The Distance between matX and matP

    MatrixXf matPointsShifted = bemSurface->rr.cast<float>() * 1.1;     // Move all points with same amout from surface
    int iNP = matPointsShifted.rows();

    mneSurfacePoints->mne_find_closest_on_surface(matPointsShifted, iNP, matResult, vecNearest, vecDist);

    // read reference
    UTILSLIB::IOUtils::read_eigen_matrix(matRef,sRef);
}

//=============================================================================================================

void TestMNEProjectToSurface::compareValue()
{
    // check if MNEProjectToSurface was able to get original points on surface
    MatrixXf matDiff = matRef.cast<float>() - matResult;
    qDebug() << "Summed Difference: " << std::abs(matDiff.sum());
    QVERIFY(std::abs(matDiff.sum()) < dEpsilon);
}

//=============================================================================================================

void TestMNEProjectToSurface::cleanupTestCase()
{
}

//=============================================================================================================
// MAIN
//=============================================================================================================

QTEST_GUILESS_MAIN(TestMNEProjectToSurface)
#include "test_mne_project_to_surface.moc"

<|endoftext|>


<commit_before>
<commit_msg>Delete output.hpp<commit_after><|endoftext|>


<commit_before>#include <iostream>
#include <cstdlib>
#include <cmath>
#include <cstdio>
#include "consts.h"
#include "circuits/circuit.h"
#include "matrix/newtonraphson.h"

using namespace std;

static const int MAX_ATTEMPTS = 10;
static const int MAX_LOOPS = 100;
static const double TOLERANCE = 1e-9;

void randomize(int numVariables, double solution[MAX_NODES+1]){
    solution[0]; // gnd
    for(int i=1; i<=numVariables; i++){
        solution[i] = (double) (rand()%10);
    }
}

double calcDistance(int numVariables, double x[MAX_NODES+1], double y[MAX_NODES+1]){
    double sum=0;
    double distance=0;
    for(int i=1; i<=numVariables ;i++) {
        sum += pow((x[i]-y[i]),2);
    }
    distance = sqrt(sum);
    return distance;
}

void printSolution(int numVariables, double solution[MAX_NODES+1]){
    for(int i=1; i<=numVariables; i++){
        cout << solution[i] << endl;
    }
    cout << endl;
}

int runNewtonRaphson(Circuit circuit,
                     double (&finalSolution)[MAX_NODES+1],
                     double t,
                     double (&lastSolution)[MAX_NODES+1]){

    int rc=0;
    int numAttempts=0;
    int numLoops=0;
    bool converged=false;
    double solution[MAX_NODES+1];
    double distance=0;
    double previousSolution[MAX_NODES+1];
    double Yn[MAX_NODES+1][MAX_NODES+2];

    while(!converged && numAttempts <= MAX_ATTEMPTS ){
        numAttempts++;
        numLoops=0;

        while(!converged && numLoops <= MAX_LOOPS){
            numLoops++;

            init(circuit.getNumVariables(), Yn);
            circuit.applyStamps(Yn,
                                previousSolution,
                                t,
                                lastSolution);

            rc = solve(circuit.getNumVariables(), Yn);
            if (rc)
                // Let's try a new randomized initial solution!
                break;

            getSolution(circuit.getNumVariables(),
                        Yn,
                        solution);

            distance = calcDistance(circuit.getNumVariables(),
                                    solution,
                                    previousSolution);
            if (distance < TOLERANCE){
                converged = true;
                solution[0] = 0; // Ground!
                copySolution(circuit.getNumVariables(),
                             solution,
                             finalSolution);
            } else {
                copySolution(circuit.getNumVariables(),
                             solution,
                             previousSolution);
            }
        }

        if (!converged){
            cout << "Newton Raphson did not converge.";
            return (EXIT_FAILURE);
        }
    }
    return 0;
}
<commit_msg>Fixes lack of randomization<commit_after>#include <iostream>
#include <cstdlib>
#include <cmath>
#include <cstdio>
#include "consts.h"
#include "circuits/circuit.h"
#include "matrix/newtonraphson.h"

using namespace std;

static const int MAX_ATTEMPTS = 10;
static const int MAX_LOOPS = 100;
static const double TOLERANCE = 1e-9;

void randomize(int numVariables, double solution[MAX_NODES+1]){
    solution[0]; // gnd
    for(int i=1; i<=numVariables; i++){
        solution[i] = (double) (rand()%10);
    }
}

double calcDistance(int numVariables, double x[MAX_NODES+1], double y[MAX_NODES+1]){
    double sum=0;
    double distance=0;
    for(int i=1; i<=numVariables ;i++) {
        sum += pow((x[i]-y[i]),2);
    }
    distance = sqrt(sum);
    return distance;
}

void printSolution(int numVariables, double solution[MAX_NODES+1]){
    for(int i=1; i<=numVariables; i++){
        cout << solution[i] << endl;
    }
    cout << endl;
}

int runNewtonRaphson(Circuit circuit,
                     double (&finalSolution)[MAX_NODES+1],
                     double t,
                     double (&lastSolution)[MAX_NODES+1]){

    int rc=0;
    int numAttempts=0;
    int numLoops=0;
    bool converged=false;
    double solution[MAX_NODES+1];
    double distance=0;
    double previousSolution[MAX_NODES+1];
    double Yn[MAX_NODES+1][MAX_NODES+2];

    while(!converged && numAttempts <= MAX_ATTEMPTS ){
        numAttempts++;
        numLoops=0;

        randomize(circuit.getNumVariables(), previousSolution);

        while(!converged && numLoops <= MAX_LOOPS){
            numLoops++;

            init(circuit.getNumVariables(), Yn);
            circuit.applyStamps(Yn,
                                previousSolution,
                                t,
                                lastSolution);

            rc = solve(circuit.getNumVariables(), Yn);
            if (rc)
                // Let's try a new randomized initial solution!
                break;

            getSolution(circuit.getNumVariables(),
                        Yn,
                        solution);

            distance = calcDistance(circuit.getNumVariables(),
                                    solution,
                                    previousSolution);
            if (distance < TOLERANCE){
                converged = true;
                solution[0] = 0; // Ground!
                copySolution(circuit.getNumVariables(),
                             solution,
                             finalSolution);
            } else {
                copySolution(circuit.getNumVariables(),
                             solution,
                             previousSolution);
            }
        }

        if (!converged){
            cout << "Newton Raphson did not converge.";
            return (EXIT_FAILURE);
        }
    }
    return 0;
}
<|endoftext|>


<commit_before>#include "parser.h"

#include "ast.h"
#include "utils.h"
#include "token.h"

#include "exceptions.h"

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

namespace sota {

    std::string Parser::Load(const std::string &filename) {
        if (!exists(filename))
            SotaException(filename + " doesn't exist or is unreadable");
        std::ifstream file(filename);
        std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
        return text;
    }

    Token Parser::Take(Token token) {
        if (token)
            _tokens.push_back(token);
        return token;
    }

    Token Parser::Emit() {
        Token token = Token();
        if (_tokens.size()) {
            token = _tokens.front();
            _tokens.pop_front();
        }
        return token;
    }

    Token Parser::Scan() {

        if (index < source.length()) {
            size_t end = index;
            Symbol *match = nullptr;
            for (auto kvp : symbols) {
                auto symbol = kvp.second;
                size_t pos = symbol->scan(symbol, source.substr(index, source.length() - index), index);
                if (pos > end || (match != nullptr && symbol->lbp > match->lbp && pos == end)) {
                    match = symbol;
                    end = pos;
                }
            }
            if (index == end)
                throw SotaException("Parser::Scan: invalid symbol");
            return Token(*match, source, index, end - index);
        }
        return Token();
    }

    /*public*/

    Parser::Parser(const Types2Symbols &symbols)
        : symbols(symbols)
        , source("")
        , index(0) {}

    Ast * Parser::ParseFile(const std::string &filename) {
        auto source = Load(filename);
        return Parse(source);
    }

    Ast * Parser::Parse(std::string source) {
        this->source = source;
        return Parse();
    }

    Ast * Parser::Parse(size_t lbp/* = 0 */) {

        Token token = Consume();

        Ast *left = token.Nud(this, &token);

        std::cout << "lbp: " << lbp << " token.symbol.lbp: " << token.symbol.lbp << std::endl;
        while (lbp < token.symbol.lbp) {
            std::cout << "lbp test passed" << std::endl;
            Token token = Consume();
            left = token.Led(this, left, &token);
        }

        return left;
    }

    Token Parser::LookAhead(size_t distance) {
        Token token;
        while(distance >= _tokens.size())
            token = Take(Scan());

        if (token)
            return token;
        return Scan();
    }

    Token Parser::Consume() {
        if (auto token = Emit())
            return token;
        return Scan();
    }

    Token Parser::Consume(const size_t &expected, const std::string &message) {
        auto token = Consume();
        if (token.symbol.type != expected)
            throw SotaException(message);
        return token;
    }

    size_t Parser::Line() {
        return 0;
    }

    size_t Parser::Column() {
        return 0;
    }
}
<commit_msg>got lookahead working but not skipping ws yet... -sai<commit_after>#include "parser.h"

#include "ast.h"
#include "utils.h"
#include "token.h"

#include "exceptions.h"

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

namespace sota {

    std::string Parser::Load(const std::string &filename) {
        if (!exists(filename))
            SotaException(filename + " doesn't exist or is unreadable");
        std::ifstream file(filename);
        std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
        return text;
    }

    Token Parser::Take(Token token) {
        if (token)
            _tokens.push_back(token);
        return token;
    }

    Token Parser::Emit() {
        Token token = Token();
        if (_tokens.size()) {
            token = _tokens.front();
            _tokens.pop_front();
        }
        return token;
    }

    Token Parser::Scan() {

        if (index < source.length()) {
            size_t end = index;
            Symbol *match = nullptr;
            for (auto kvp : symbols) {
                auto symbol = kvp.second;
                size_t pos = symbol->scan(symbol, source.substr(index, source.length() - index), index);
                if (pos > end || (match != nullptr && symbol->lbp > match->lbp && pos == end)) {
                    match = symbol;
                    end = pos;
                }
            }
            if (index == end)
                throw SotaException("Parser::Scan: invalid symbol");
            return Token(*match, source, index, end - index);
        }
        return Token();
    }

    /*public*/

    Parser::Parser(const Types2Symbols &symbols)
        : symbols(symbols)
        , source("")
        , index(0) {}

    Ast * Parser::ParseFile(const std::string &filename) {
        auto source = Load(filename);
        return Parse(source);
    }

    Ast * Parser::Parse(std::string source) {
        this->source = source;
        return Parse();
    }

    Ast * Parser::Parse(size_t lbp/* = 0 */) {

        Token curr = Consume();
        std::cout << "curr: " << curr << std::endl;

        Ast *left = curr.Nud(this, &curr);

        Token next = LookAhead(1);
        std::cout << "next: " << next << std::endl;
        std::cout << "lbp: " << lbp << " next.symbol.lbp: " << next.symbol.lbp << std::endl;

        while (lbp < next.symbol.lbp) {
            std::cout << "lbp test passed" << std::endl;
            Token next  = Consume();
            left = next.Led(this, left, &next);
        }

        return left;
    }

    Token Parser::LookAhead(size_t distance) {
        if (distance == 0) {
            return Scan();
        }

        Token token;
        while(distance > _tokens.size()) {
            token = Take(Scan());
        }

        return token;
    }

    Token Parser::Consume() {
        auto la = LookAhead(1);
        index += la.length;
        return   Emit();
    }

    Token Parser::Consume(const size_t &expected, const std::string &message) {
        auto token = Consume();
        if (token.symbol.type != expected)
            throw SotaException(message);
        return token;
    }

    size_t Parser::Line() {
        return 0;
    }

    size_t Parser::Column() {
        return 0;
    }
}
<|endoftext|>


<commit_before>#include <stdio.h>
#include "ys_matrixTool.h"
#include "ys_matrix.h"

using namespace ys;

int main(void) {
    DMatrix<int> matrix1;
    matrix1[0][0] = 7;
    matrix1[0][1] = 6;
    matrix1[0][2] = 2;
    matrix1[1][0] = 3;
    matrix1[1][1] = 2;
    matrix1[1][2] = 5;

    for (int i = 0; i < matrix1.getDimX(); ++i) {
    for (int j = 0; j < matrix1.getDimY(); ++j) {
        printf ("%d ", matrix1[i][j]);
    }
    printf ("\n");
    }
    printf ("----------------------------\n");

    DMatrix<int> matrix2;
    matrix1[0][0] = 8;
    matrix1[0][1] = 9;
    matrix1[1][0] = 1;
    matrix1[1][1] = 6;
    matrix1[2][0] = 3;
    matrix1[2][1] = 8;

    for (int i = 0; i < matrix2.getDimX(); ++i) {
    for (int j = 0; j < matrix2.getDimY(); ++j) {
        printf ("%d ", matrix2[i][j]);
    }
    printf ("\n");
    }
    printf ("----------------------------\n");

    DMatrix<int>* matrix3 = Matrixs<int>::mul(matrix1, matrix2);

    for (int i = 0; i < matrix3->getDimX(); ++i) {
    for (int j = 0; j < matrix3->getDimY(); ++j) {
        printf ("%d ", matrix3[i][j]);
    }
    printf ("\n");
    }

    delete matrix3;
    return 0;
}
<commit_msg>modify<commit_after>#include <stdio.h>
#include "ys_matrixTool.h"
#include "ys_matrix.h"

using namespace ys;

int main(void) {
    DMatrix<int> matrix1(2, 3);
    printf ("1\n");
    matrix1[0][0] = 7;
    printf ("1\n");
    matrix1[0][1] = 6;
    matrix1[0][2] = 2;
    matrix1[1][0] = 3;
    matrix1[1][1] = 2;
    matrix1[1][2] = 5;

    for (int i = 0; i < matrix1.getDimX(); ++i) {
    for (int j = 0; j < matrix1.getDimY(); ++j) {
        printf ("%d ", matrix1[i][j]);
    }
    printf ("\n");
    }
    printf ("----------------------------\n");

    DMatrix<int> matrix2(3, 4);
    matrix1[0][0] = 8;
    matrix1[0][1] = 9;
    matrix1[1][0] = 1;
    matrix1[1][1] = 6;
    matrix1[2][0] = 3;
    matrix1[2][1] = 8;

    for (int i = 0; i < matrix2.getDimX(); ++i) {
    for (int j = 0; j < matrix2.getDimY(); ++j) {
        printf ("%d ", matrix2[i][j]);
    }
    printf ("\n");
    }
    printf ("----------------------------\n");

    DMatrix<int>* matrix3 = Matrixs<int>::mul(matrix1, matrix2);

    for (int i = 0; i < matrix3->getDimX(); ++i) {
    for (int j = 0; j < matrix3->getDimY(); ++j) {
        printf ("%d ", matrix3[i][j]);
    }
    printf ("\n");
    }

    delete matrix3;
    return 0;
}
<|endoftext|>


<commit_before>#include "parser.hpp"

#include "ast_data.hpp"

namespace klang {

Parser::Parser(TokenVector tokens)
    : tokens_(std::move(tokens)),
      current_(std::begin(tokens_))
{}

bool Parser::parse_symbol(const char* str) {
  if (current_type() == TokenType::SYMBOL &&
      current_string() == std::string{str}) {
    advance(1);
    return true;
  } else {
    return false;
  }
}

ast::IdentifierPtr Parser::parse_identifier() {
  if (current_type() == TokenType::IDENTIFIER) {
    advance(1);
    return make_unique<ast::IdentifierData>(current_string());
  } else {
    return nullptr;
  }
}

ast::TypePtr Parser::parse_type() {
  if (current_type() == TokenType::SYMBOL) {
    advance(1);
    return make_unique<ast::TypeData>(current_string());
  } else {
    return nullptr;
  }
}

ast::IntegerLiteralPtr Parser::parse_integer_literal() {
  if (current_type() == TokenType::NUMBER) {
    advance(1);
    return make_unique<ast::IntegerLiteralData>(current_string());
  } else {
    return nullptr;
  }
}

ast::CharacterLiteralPtr Parser::parse_character_literal() {
  if (current_type() == TokenType::CHARACTER) {
    advance(1);
    return make_unique<ast::CharacterLiteralData>(current_string());
  } else {
    return nullptr;
  }
}

ast::StringLiteralPtr Parser::parse_string_literal() {
  if (current_type() == TokenType::STRING) {
    advance(1);
    return make_unique<ast::StringLiteralData>(current_string());
  } else {
    return nullptr;
  }
}

ast::TranslationUnitPtr Parser::parse_translation_unit() {
  std::vector<ast::FunctionDefinitionPtr> functions;
  while (auto function = parse_function_definition()) {
    functions.push_back(std::move(function));
  }
  return make_unique<ast::TranslationUnitData>(std::move(functions));
}

ast::FunctionDefinitionPtr Parser::parse_function_definition() {
  const auto s = snapshot();
  if (parse_symbol("def")) {
    if (auto function_name = parse_identifier()) {
      if (parse_symbol("(")) {
        if (auto arguments = parse_argument_list()) {
          if (parse_symbol(")") && parse_symbol("->") && parse_symbol("(")) {
            if (auto return_type = parse_type()) {
              if (parse_symbol(")")) {
                if (auto function_body = parse_compound_statement()) {
                  return make_unique<ast::FunctionDefinitionData>(
                      std::move(function_name),
                      std::move(arguments),
                      std::move(return_type),
                      std::move(function_body));
                }
              }
            }
          }
        }
      }
    }
  }
  rewind(s);
  return nullptr;
}

ast::ArgumentListPtr Parser::parse_argument_list() {
  std::vector<ast::ArgumentPtr> arguments;
  if (auto first_argument = parse_argument()) {
    arguments.push_back(std::move(first_argument));
    while (true) {
      const auto s = snapshot();
      if (parse_symbol(",")) {
        if (auto argument = parse_argument()) {
          arguments.push_back(std::move(argument));
          continue;
        }
      }
      rewind(s);
      break;
    }
  }
  return make_unique<ast::ArgumentListData>(std::move(arguments));
}

ast::ArgumentPtr Parser::parse_argument() {
  const auto s = snapshot();
  if (auto argument_type = parse_type()) {
    if (auto argument_name = parse_identifier()) {
      return make_unique<ast::ArgumentData>(std::move(argument_type),
                                            std::move(argument_name));
    }
  }
  rewind(s);
  return nullptr;
}

ast::StatementPtr Parser::parse_statement() {
  if (auto statement = parse_compound_statement()) {
    return std::move(statement);
  } else if (auto statement = parse_if_statement()){
    return std::move(statement);
  } else if (auto statement = parse_while_statement()){
    return std::move(statement);
  } else if (auto statement = parse_for_statement()){
    return std::move(statement);
  } else if (auto statement = parse_return_statement()){
    return std::move(statement);
  } else if (auto statement = parse_break_statement()){
    return std::move(statement);
  } else if (auto statement = parse_continue_statement()){
    return std::move(statement);
  } else if (auto statement = parse_variable_definition_statement()){
    return std::move(statement);
  } else if (auto statement = parse_expression_statement()){
    return std::move(statement);
  }
  return nullptr;
}

ast::CompoundStatementPtr Parser::parse_compound_statement() {
  std::vector<ast::StatementPtr> statements;
  const auto s = snapshot();
  if (parse_symbol("{")) {
    while (auto statement = parse_statement()) {
      statements.push_back(std::move(statement));
    }
    if (parse_symbol("}")) {
      return make_unique<ast::CompoundStatementData>(std::move(statements));
    }
  }
  rewind(s);
  return nullptr;
}

ast::IfStatementPtr Parser::parse_if_statement() {
  const auto s = snapshot();
  if (parse_symbol("if") && parse_symbol("(")) {
    if (auto condition = parse_expression()) {
      if (parse_symbol(")")) {
        if (auto compound_statement = parse_compound_statement()) {
          return make_unique<ast::IfStatementData>(
              std::move(condition),
              std::move(compound_statement),
              parse_else_statement());
        }
      }
    }
  }
  rewind(s);
  return nullptr;
}

ast::IfStatementPtr Parser::parse_else_statement() {
  const auto s = snapshot();
  if (parse_symbol("else")) {
    if (auto else_if_statement = parse_if_statement()) {
      return std::move(else_if_statement);
    } else if (auto compound_statement = parse_compound_statement()) {
      return make_unique<ast::ElseStatementData>(std::move(compound_statement));
    }
  }
  rewind(s);
  return nullptr;
}

TokenType Parser::current_type() const {
  return is_eof() ? TokenType::IGNORE : current_->type();
}

std::string Parser::current_string() const {
  static const std::string empty_string{""};
  return is_eof() ? empty_string : current_->str();
}

bool Parser::is_eof() const {
  using std::end;
  return current_ == end(tokens_);
}

bool Parser::advance(int count) {
  using std::begin;
  using std::end;
  const auto b = begin(tokens_);
  const auto e = end(tokens_);
  while (count < 0) {
    if (current_ != b) {
      ++count;
      --current_;
    } else {
      return false;
    }
  }
  while (0 < count) {
    if (current_ != e) {
      --count;
      ++current_;
    } else {
      return false;
    }
  }
  return true;
}

auto Parser::snapshot() const -> Pointer {
  return current_;
}

void Parser::rewind(Pointer p) {
  current_ = p;
}

}  // namespace klang
<commit_msg>Add parse_while_statement() member<commit_after>#include "parser.hpp"

#include "ast_data.hpp"

namespace klang {

Parser::Parser(TokenVector tokens)
    : tokens_(std::move(tokens)),
      current_(std::begin(tokens_))
{}

bool Parser::parse_symbol(const char* str) {
  if (current_type() == TokenType::SYMBOL &&
      current_string() == std::string{str}) {
    advance(1);
    return true;
  } else {
    return false;
  }
}

ast::IdentifierPtr Parser::parse_identifier() {
  if (current_type() == TokenType::IDENTIFIER) {
    advance(1);
    return make_unique<ast::IdentifierData>(current_string());
  } else {
    return nullptr;
  }
}

ast::TypePtr Parser::parse_type() {
  if (current_type() == TokenType::SYMBOL) {
    advance(1);
    return make_unique<ast::TypeData>(current_string());
  } else {
    return nullptr;
  }
}

ast::IntegerLiteralPtr Parser::parse_integer_literal() {
  if (current_type() == TokenType::NUMBER) {
    advance(1);
    return make_unique<ast::IntegerLiteralData>(current_string());
  } else {
    return nullptr;
  }
}

ast::CharacterLiteralPtr Parser::parse_character_literal() {
  if (current_type() == TokenType::CHARACTER) {
    advance(1);
    return make_unique<ast::CharacterLiteralData>(current_string());
  } else {
    return nullptr;
  }
}

ast::StringLiteralPtr Parser::parse_string_literal() {
  if (current_type() == TokenType::STRING) {
    advance(1);
    return make_unique<ast::StringLiteralData>(current_string());
  } else {
    return nullptr;
  }
}

ast::TranslationUnitPtr Parser::parse_translation_unit() {
  std::vector<ast::FunctionDefinitionPtr> functions;
  while (auto function = parse_function_definition()) {
    functions.push_back(std::move(function));
  }
  return make_unique<ast::TranslationUnitData>(std::move(functions));
}

ast::FunctionDefinitionPtr Parser::parse_function_definition() {
  const auto s = snapshot();
  if (parse_symbol("def")) {
    if (auto function_name = parse_identifier()) {
      if (parse_symbol("(")) {
        if (auto arguments = parse_argument_list()) {
          if (parse_symbol(")") && parse_symbol("->") && parse_symbol("(")) {
            if (auto return_type = parse_type()) {
              if (parse_symbol(")")) {
                if (auto function_body = parse_compound_statement()) {
                  return make_unique<ast::FunctionDefinitionData>(
                      std::move(function_name),
                      std::move(arguments),
                      std::move(return_type),
                      std::move(function_body));
                }
              }
            }
          }
        }
      }
    }
  }
  rewind(s);
  return nullptr;
}

ast::ArgumentListPtr Parser::parse_argument_list() {
  std::vector<ast::ArgumentPtr> arguments;
  if (auto first_argument = parse_argument()) {
    arguments.push_back(std::move(first_argument));
    while (true) {
      const auto s = snapshot();
      if (parse_symbol(",")) {
        if (auto argument = parse_argument()) {
          arguments.push_back(std::move(argument));
          continue;
        }
      }
      rewind(s);
      break;
    }
  }
  return make_unique<ast::ArgumentListData>(std::move(arguments));
}

ast::ArgumentPtr Parser::parse_argument() {
  const auto s = snapshot();
  if (auto argument_type = parse_type()) {
    if (auto argument_name = parse_identifier()) {
      return make_unique<ast::ArgumentData>(std::move(argument_type),
                                            std::move(argument_name));
    }
  }
  rewind(s);
  return nullptr;
}

ast::StatementPtr Parser::parse_statement() {
  if (auto statement = parse_compound_statement()) {
    return std::move(statement);
  } else if (auto statement = parse_if_statement()){
    return std::move(statement);
  } else if (auto statement = parse_while_statement()){
    return std::move(statement);
  } else if (auto statement = parse_for_statement()){
    return std::move(statement);
  } else if (auto statement = parse_return_statement()){
    return std::move(statement);
  } else if (auto statement = parse_break_statement()){
    return std::move(statement);
  } else if (auto statement = parse_continue_statement()){
    return std::move(statement);
  } else if (auto statement = parse_variable_definition_statement()){
    return std::move(statement);
  } else if (auto statement = parse_expression_statement()){
    return std::move(statement);
  }
  return nullptr;
}

ast::CompoundStatementPtr Parser::parse_compound_statement() {
  std::vector<ast::StatementPtr> statements;
  const auto s = snapshot();
  if (parse_symbol("{")) {
    while (auto statement = parse_statement()) {
      statements.push_back(std::move(statement));
    }
    if (parse_symbol("}")) {
      return make_unique<ast::CompoundStatementData>(std::move(statements));
    }
  }
  rewind(s);
  return nullptr;
}

ast::IfStatementPtr Parser::parse_if_statement() {
  const auto s = snapshot();
  if (parse_symbol("if") && parse_symbol("(")) {
    if (auto condition = parse_expression()) {
      if (parse_symbol(")")) {
        if (auto compound_statement = parse_compound_statement()) {
          return make_unique<ast::IfStatementData>(
              std::move(condition),
              std::move(compound_statement),
              parse_else_statement());
        }
      }
    }
  }
  rewind(s);
  return nullptr;
}

ast::IfStatementPtr Parser::parse_else_statement() {
  const auto s = snapshot();
  if (parse_symbol("else")) {
    if (auto else_if_statement = parse_if_statement()) {
      return std::move(else_if_statement);
    } else if (auto compound_statement = parse_compound_statement()) {
      return make_unique<ast::ElseStatementData>(std::move(compound_statement));
    }
  }
  rewind(s);
  return nullptr;
}

ast::WhileStatementPtr Parser::parse_while_statement() {
  const auto s = snapshot();
  if (parse_symbol("while") && parse_symbol("(")) {
    if (auto condition = parse_expression()) {
      if (parse_symbol(")")) {
        if (auto compound_statement = parse_compound_statement()) {
          return make_unique<ast::WhileStatementData>(
              std::move(condition),
              std::move(compound_statement));
        }
      }
    }
  }
  rewind(s);
  return nullptr;
}

TokenType Parser::current_type() const {
  return is_eof() ? TokenType::IGNORE : current_->type();
}

std::string Parser::current_string() const {
  static const std::string empty_string{""};
  return is_eof() ? empty_string : current_->str();
}

bool Parser::is_eof() const {
  using std::end;
  return current_ == end(tokens_);
}

bool Parser::advance(int count) {
  using std::begin;
  using std::end;
  const auto b = begin(tokens_);
  const auto e = end(tokens_);
  while (count < 0) {
    if (current_ != b) {
      ++count;
      --current_;
    } else {
      return false;
    }
  }
  while (0 < count) {
    if (current_ != e) {
      --count;
      ++current_;
    } else {
      return false;
    }
  }
  return true;
}

auto Parser::snapshot() const -> Pointer {
  return current_;
}

void Parser::rewind(Pointer p) {
  current_ = p;
}

}  // namespace klang
<|endoftext|>


<commit_before>#include "pbview.h"

#include <cinttypes>
#include <cstdio>
#include <cstring>
#include <curses.h>
#include <iostream>
#include <sstream>

#include "config.h"
#include "configcontainer.h"
#include "dllist.h"
#include "download.h"
#include "fmtstrformatter.h"
#include "help.h"
#include "logger.h"
#include "pbcontroller.h"
#include "poddlthread.h"
#include "strprintf.h"
#include "utils.h"

using namespace newsboat;

namespace podboat {

PbView::PbView(PbController* c)
	: ctrl(c)
	, dllist_form(dllist_str)
	, help_form(help_str)
	, keys(0)
{
	if (getenv("ESCDELAY") == nullptr) {
		set_escdelay(25);
	}
}

PbView::~PbView()
{
	Stfl::reset();
}

void PbView::run(bool auto_download)
{
	bool quit = false;

	set_dllist_keymap_hint();

	do {
		if (ctrl->view_update_necessary()) {
			const double total_kbps = ctrl->get_total_kbps();
			const auto speed = get_speed_human_readable(total_kbps);

			char parbuf[128] = "";
			if (ctrl->get_maxdownloads() > 1) {
				snprintf(parbuf,
					sizeof(parbuf),
					_(" - %u parallel downloads"),
					ctrl->get_maxdownloads());
			}

			char buf[1024];
			snprintf(buf,
				sizeof(buf),
				_("Queue (%u downloads in progress, %u total) "
					"- %.2f %s total%s"),
				static_cast<unsigned int>(
					ctrl->downloads_in_progress()),
				static_cast<unsigned int>(
					ctrl->downloads().size()),
				speed.first,
				speed.second.c_str(),
				parbuf);

			dllist_form.set("head", buf);

			LOG(Level::DEBUG,
				"PbView::run: updating view... "
				"downloads().size() "
				"= %" PRIu64,
				static_cast<uint64_t>(ctrl->downloads().size()));

			std::string code = "{list";
			std::string formatstring = ctrl->get_formatstr();

			dllist_form.run(-3); // compute all widget dimensions
			unsigned int width = utils::to_u(dllist_form.get("dls:w"));

			unsigned int i = 0;
			for (const auto& dl : ctrl->downloads()) {
				auto lbuf = format_line(formatstring, dl, i, width);
				code.append(
					strprintf::fmt("{listitem[%u] text:%s}",
						i,
						Stfl::quote(lbuf)));
				i++;
			}

			code.append("}");

			dllist_form.modify("dls", "replace_inner", code);

			ctrl->set_view_update_necessary(false);
		}

		const char* event = dllist_form.run(500);

		if (auto_download) {
			if (ctrl->get_maxdownloads() >
				ctrl->downloads_in_progress()) {
				ctrl->start_downloads();
			}
		}

		if (!event || strcmp(event, "TIMEOUT") == 0) {
			continue;
		}

		Operation op = keys->get_operation(event, "podbeuter");

		if (dllist_form.get("msg").length() > 0) {
			dllist_form.set("msg", "");
			ctrl->set_view_update_necessary(true);
		}

		switch (op) {
		case OP_PB_TOGGLE_DLALL:
			auto_download = !auto_download;
			break;
		case OP_HARDQUIT:
		case OP_QUIT:
			if (ctrl->downloads_in_progress() > 0) {
				dllist_form.set("msg",
					_("Error: can't quit: download(s) in "
						"progress."));
				ctrl->set_view_update_necessary(true);
			} else {
				quit = true;
			}
			break;
		case OP_PB_MOREDL:
			ctrl->increase_parallel_downloads();
			break;
		case OP_PB_LESSDL:
			ctrl->decrease_parallel_downloads();
			break;
		case OP_PB_DOWNLOAD: {
			std::istringstream os(dllist_form.get("dlposname"));
			int idx = -1;
			os >> idx;
			if (idx != -1) {
				if (ctrl->downloads()[idx].status() !=
					DlStatus::DOWNLOADING) {
					std::thread t{PodDlThread(
							&ctrl->downloads()[idx],
							ctrl->get_cfgcont())};
					t.detach();
				}
			}
		}
		break;
		case OP_PB_PLAY: {
			std::istringstream os(dllist_form.get("dlposname"));
			int idx = -1;
			os >> idx;
			if (idx != -1) {
				DlStatus status =
					ctrl->downloads()[idx].status();
				if (status == DlStatus::FINISHED ||
					status == DlStatus::PLAYED ||
					status == DlStatus::READY) {
					ctrl->play_file(ctrl->downloads()[idx]
						.filename());
					ctrl->downloads()[idx].set_status(
						DlStatus::PLAYED);
				} else {
					dllist_form.set("msg",
						_("Error: download needs to be "
							"finished before the file "
							"can be played."));
				}
			}
		}
		break;
		case OP_PB_MARK_FINISHED: {
			std::istringstream os(dllist_form.get("dlposname"));
			int idx = -1;
			os >> idx;
			if (idx != -1) {
				DlStatus status =
					ctrl->downloads()[idx].status();
				if (status == DlStatus::PLAYED) {
					ctrl->downloads()[idx].set_status(
						DlStatus::FINISHED);
				}
			}
		}
		break;
		case OP_PB_CANCEL: {
			std::istringstream os(dllist_form.get("dlposname"));
			int idx = -1;
			os >> idx;
			if (idx != -1) {
				if (ctrl->downloads()[idx].status() ==
					DlStatus::DOWNLOADING) {
					ctrl->downloads()[idx].set_status(
						DlStatus::CANCELLED);
				}
			}
		}
		break;
		case OP_PB_DELETE: {
			std::istringstream os(dllist_form.get("dlposname"));
			int idx = -1;
			os >> idx;
			if (idx != -1) {
				if (ctrl->downloads()[idx].status() !=
					DlStatus::DOWNLOADING) {
					ctrl->downloads()[idx].set_status(
						DlStatus::DELETED);
				}
			}
		}
		break;
		case OP_PB_PURGE:
			if (ctrl->downloads_in_progress() > 0) {
				dllist_form.set("msg",
					_("Error: unable to perform operation: "
						"download(s) in progress."));
			} else {
				ctrl->purge_queue();
			}
			ctrl->set_view_update_necessary(true);
			break;
		case OP_HELP:
			run_help();
			break;
		default:
			break;
		}

	} while (!quit);
}

void PbView::set_bindings()
{
	if (keys) {
		std::string upkey("** ");
		upkey.append(keys->getkey(OP_SK_UP, "podbeuter"));
		std::string downkey("** ");
		downkey.append(keys->getkey(OP_SK_DOWN, "podbeuter"));
		std::string pgupkey("** ");
		pgupkey.append(keys->getkey(OP_SK_PGUP, "podbeuter"));
		std::string pgdownkey("** ");
		pgdownkey.append(keys->getkey(OP_SK_PGDOWN, "podbeuter"));
		std::string homekey("** ");
		homekey.append(keys->getkey(OP_SK_HOME, "podbeuter"));
		std::string endkey("** ");
		endkey.append(keys->getkey(OP_SK_END, "podbeuter"));

		dllist_form.set("bind_up", upkey);
		dllist_form.set("bind_down", downkey);
		dllist_form.set("bind_page_up", pgupkey);
		dllist_form.set("bind_page_down", pgdownkey);
		dllist_form.set("bind_home", homekey);
		dllist_form.set("bind_end", endkey);

		help_form.set("bind_up", upkey);
		help_form.set("bind_down", downkey);
		help_form.set("bind_page_up", pgupkey);
		help_form.set("bind_page_down", pgdownkey);
		help_form.set("bind_home", homekey);
		help_form.set("bind_end", endkey);
	}
}

std::pair<double, std::string> PbView::get_speed_human_readable(double kbps)
{
	if (kbps < 1024) {
		return std::make_pair(kbps, _("KB/s"));
	} else if (kbps < 1024 * 1024) {
		return std::make_pair(kbps / 1024, _("MB/s"));
	} else {
		return std::make_pair(kbps / 1024 / 1024, _("GB/s"));
	}
}

void PbView::run_help()
{
	set_help_keymap_hint();

	help_form.set("head", _("Help"));

	std::vector<KeyMapDesc> descs;
	keys->get_keymap_descriptions(descs, KM_PODBOAT);

	std::string code = "{list";

	for (const auto& desc : descs) {
		std::string line = "{listitem text:";

		std::string descline;
		descline.append(desc.key);
		descline.append(8 - desc.key.length(), ' ');
		descline.append(desc.cmd);
		descline.append(24 - desc.cmd.length(), ' ');
		descline.append(desc.desc);
		line.append(Stfl::quote(descline));

		line.append("}");

		code.append(line);
	}

	code.append("}");

	help_form.modify("helptext", "replace_inner", code);

	bool quit = false;

	do {
		const char* event = help_form.run(0);
		if (!event) {
			continue;
		}

		Operation op = keys->get_operation(event, "help");

		switch (op) {
		case OP_HARDQUIT:
		case OP_QUIT:
			quit = true;
			break;
		default:
			break;
		}
	} while (!quit);
}

std::string PbView::prepare_keymaphint(KeyMapHintEntry* hints)
{
	std::string keymap_hint;
	for (int i = 0; hints[i].op != OP_NIL; ++i) {
		keymap_hint.append(keys->getkey(hints[i].op, "podbeuter"));
		keymap_hint.append(":");
		keymap_hint.append(hints[i].text);
		keymap_hint.append(" ");
	}
	return keymap_hint;
}

void PbView::set_help_keymap_hint()
{
	KeyMapHintEntry hints[] = {{OP_QUIT, _("Quit")}, {OP_NIL, nullptr}};
	std::string keymap_hint = prepare_keymaphint(hints);
	help_form.set("help", keymap_hint);
}

void PbView::set_dllist_keymap_hint()
{
	KeyMapHintEntry hints[] = {{OP_QUIT, _("Quit")},
		{OP_PB_DOWNLOAD, _("Download")},
		{OP_PB_CANCEL, _("Cancel")},
		{OP_PB_DELETE, _("Delete")},
		{OP_PB_PURGE, _("Purge Finished")},
		{OP_PB_TOGGLE_DLALL, _("Toggle Automatic Download")},
		{OP_PB_PLAY, _("Play")},
		{OP_PB_MARK_FINISHED, _("Mark as Finished")},
		{OP_HELP, _("Help")},
		{OP_NIL, nullptr}
	};

	std::string keymap_hint = prepare_keymaphint(hints);
	dllist_form.set("help", keymap_hint);
}

std::string PbView::format_line(const std::string& podlist_format,
	const Download& dl,
	unsigned int pos,
	unsigned int width)
{
	FmtStrFormatter fmt;

	const double speed_kbps = dl.kbps();
	const auto speed = get_speed_human_readable(speed_kbps);

	fmt.register_fmt('i', strprintf::fmt("%u", pos + 1));
	fmt.register_fmt('d',
		strprintf::fmt("%.1f", dl.current_size() / (1024 * 1024)));
	fmt.register_fmt(
		't', strprintf::fmt("%.1f", dl.total_size() / (1024 * 1024)));
	fmt.register_fmt('p', strprintf::fmt("%.1f", dl.percents_finished()));
	fmt.register_fmt('k', strprintf::fmt("%.2f", speed_kbps));
	fmt.register_fmt('K', strprintf::fmt("%.2f %s", speed.first, speed.second));
	fmt.register_fmt('S', strprintf::fmt("%s", dl.status_text()));
	fmt.register_fmt('u', strprintf::fmt("%s", dl.url()));
	fmt.register_fmt('F', strprintf::fmt("%s", dl.filename()));
	fmt.register_fmt('b', strprintf::fmt("%s", dl.basename()));

	auto formattedLine = fmt.do_format(podlist_format, width);
	return formattedLine;
}

} // namespace podboat
<commit_msg>Podboat: Hide cursor<commit_after>#include "pbview.h"

#include <cinttypes>
#include <cstdio>
#include <cstring>
#include <curses.h>
#include <iostream>
#include <sstream>

#include "config.h"
#include "configcontainer.h"
#include "dllist.h"
#include "download.h"
#include "fmtstrformatter.h"
#include "help.h"
#include "logger.h"
#include "pbcontroller.h"
#include "poddlthread.h"
#include "strprintf.h"
#include "utils.h"

using namespace newsboat;

namespace podboat {

PbView::PbView(PbController* c)
	: ctrl(c)
	, dllist_form(dllist_str)
	, help_form(help_str)
	, keys(0)
{
	if (getenv("ESCDELAY") == nullptr) {
		set_escdelay(25);
	}
}

PbView::~PbView()
{
	Stfl::reset();
}

void PbView::run(bool auto_download)
{
	bool quit = false;

	// Make sure curses is initialized
	dllist_form.run(-3);
	// Hide cursor using curses
	curs_set(0);

	set_dllist_keymap_hint();

	do {
		if (ctrl->view_update_necessary()) {
			const double total_kbps = ctrl->get_total_kbps();
			const auto speed = get_speed_human_readable(total_kbps);

			char parbuf[128] = "";
			if (ctrl->get_maxdownloads() > 1) {
				snprintf(parbuf,
					sizeof(parbuf),
					_(" - %u parallel downloads"),
					ctrl->get_maxdownloads());
			}

			char buf[1024];
			snprintf(buf,
				sizeof(buf),
				_("Queue (%u downloads in progress, %u total) "
					"- %.2f %s total%s"),
				static_cast<unsigned int>(
					ctrl->downloads_in_progress()),
				static_cast<unsigned int>(
					ctrl->downloads().size()),
				speed.first,
				speed.second.c_str(),
				parbuf);

			dllist_form.set("head", buf);

			LOG(Level::DEBUG,
				"PbView::run: updating view... "
				"downloads().size() "
				"= %" PRIu64,
				static_cast<uint64_t>(ctrl->downloads().size()));

			std::string code = "{list";
			std::string formatstring = ctrl->get_formatstr();

			dllist_form.run(-3); // compute all widget dimensions
			unsigned int width = utils::to_u(dllist_form.get("dls:w"));

			unsigned int i = 0;
			for (const auto& dl : ctrl->downloads()) {
				auto lbuf = format_line(formatstring, dl, i, width);
				code.append(
					strprintf::fmt("{listitem[%u] text:%s}",
						i,
						Stfl::quote(lbuf)));
				i++;
			}

			code.append("}");

			dllist_form.modify("dls", "replace_inner", code);

			ctrl->set_view_update_necessary(false);
		}

		const char* event = dllist_form.run(500);

		if (auto_download) {
			if (ctrl->get_maxdownloads() >
				ctrl->downloads_in_progress()) {
				ctrl->start_downloads();
			}
		}

		if (!event || strcmp(event, "TIMEOUT") == 0) {
			continue;
		}

		Operation op = keys->get_operation(event, "podbeuter");

		if (dllist_form.get("msg").length() > 0) {
			dllist_form.set("msg", "");
			ctrl->set_view_update_necessary(true);
		}

		switch (op) {
		case OP_PB_TOGGLE_DLALL:
			auto_download = !auto_download;
			break;
		case OP_HARDQUIT:
		case OP_QUIT:
			if (ctrl->downloads_in_progress() > 0) {
				dllist_form.set("msg",
					_("Error: can't quit: download(s) in "
						"progress."));
				ctrl->set_view_update_necessary(true);
			} else {
				quit = true;
			}
			break;
		case OP_PB_MOREDL:
			ctrl->increase_parallel_downloads();
			break;
		case OP_PB_LESSDL:
			ctrl->decrease_parallel_downloads();
			break;
		case OP_PB_DOWNLOAD: {
			std::istringstream os(dllist_form.get("dlposname"));
			int idx = -1;
			os >> idx;
			if (idx != -1) {
				if (ctrl->downloads()[idx].status() !=
					DlStatus::DOWNLOADING) {
					std::thread t{PodDlThread(
							&ctrl->downloads()[idx],
							ctrl->get_cfgcont())};
					t.detach();
				}
			}
		}
		break;
		case OP_PB_PLAY: {
			std::istringstream os(dllist_form.get("dlposname"));
			int idx = -1;
			os >> idx;
			if (idx != -1) {
				DlStatus status =
					ctrl->downloads()[idx].status();
				if (status == DlStatus::FINISHED ||
					status == DlStatus::PLAYED ||
					status == DlStatus::READY) {
					ctrl->play_file(ctrl->downloads()[idx]
						.filename());
					ctrl->downloads()[idx].set_status(
						DlStatus::PLAYED);
				} else {
					dllist_form.set("msg",
						_("Error: download needs to be "
							"finished before the file "
							"can be played."));
				}
			}
		}
		break;
		case OP_PB_MARK_FINISHED: {
			std::istringstream os(dllist_form.get("dlposname"));
			int idx = -1;
			os >> idx;
			if (idx != -1) {
				DlStatus status =
					ctrl->downloads()[idx].status();
				if (status == DlStatus::PLAYED) {
					ctrl->downloads()[idx].set_status(
						DlStatus::FINISHED);
				}
			}
		}
		break;
		case OP_PB_CANCEL: {
			std::istringstream os(dllist_form.get("dlposname"));
			int idx = -1;
			os >> idx;
			if (idx != -1) {
				if (ctrl->downloads()[idx].status() ==
					DlStatus::DOWNLOADING) {
					ctrl->downloads()[idx].set_status(
						DlStatus::CANCELLED);
				}
			}
		}
		break;
		case OP_PB_DELETE: {
			std::istringstream os(dllist_form.get("dlposname"));
			int idx = -1;
			os >> idx;
			if (idx != -1) {
				if (ctrl->downloads()[idx].status() !=
					DlStatus::DOWNLOADING) {
					ctrl->downloads()[idx].set_status(
						DlStatus::DELETED);
				}
			}
		}
		break;
		case OP_PB_PURGE:
			if (ctrl->downloads_in_progress() > 0) {
				dllist_form.set("msg",
					_("Error: unable to perform operation: "
						"download(s) in progress."));
			} else {
				ctrl->purge_queue();
			}
			ctrl->set_view_update_necessary(true);
			break;
		case OP_HELP:
			run_help();
			break;
		default:
			break;
		}

	} while (!quit);
}

void PbView::set_bindings()
{
	if (keys) {
		std::string upkey("** ");
		upkey.append(keys->getkey(OP_SK_UP, "podbeuter"));
		std::string downkey("** ");
		downkey.append(keys->getkey(OP_SK_DOWN, "podbeuter"));
		std::string pgupkey("** ");
		pgupkey.append(keys->getkey(OP_SK_PGUP, "podbeuter"));
		std::string pgdownkey("** ");
		pgdownkey.append(keys->getkey(OP_SK_PGDOWN, "podbeuter"));
		std::string homekey("** ");
		homekey.append(keys->getkey(OP_SK_HOME, "podbeuter"));
		std::string endkey("** ");
		endkey.append(keys->getkey(OP_SK_END, "podbeuter"));

		dllist_form.set("bind_up", upkey);
		dllist_form.set("bind_down", downkey);
		dllist_form.set("bind_page_up", pgupkey);
		dllist_form.set("bind_page_down", pgdownkey);
		dllist_form.set("bind_home", homekey);
		dllist_form.set("bind_end", endkey);

		help_form.set("bind_up", upkey);
		help_form.set("bind_down", downkey);
		help_form.set("bind_page_up", pgupkey);
		help_form.set("bind_page_down", pgdownkey);
		help_form.set("bind_home", homekey);
		help_form.set("bind_end", endkey);
	}
}

std::pair<double, std::string> PbView::get_speed_human_readable(double kbps)
{
	if (kbps < 1024) {
		return std::make_pair(kbps, _("KB/s"));
	} else if (kbps < 1024 * 1024) {
		return std::make_pair(kbps / 1024, _("MB/s"));
	} else {
		return std::make_pair(kbps / 1024 / 1024, _("GB/s"));
	}
}

void PbView::run_help()
{
	set_help_keymap_hint();

	help_form.set("head", _("Help"));

	std::vector<KeyMapDesc> descs;
	keys->get_keymap_descriptions(descs, KM_PODBOAT);

	std::string code = "{list";

	for (const auto& desc : descs) {
		std::string line = "{listitem text:";

		std::string descline;
		descline.append(desc.key);
		descline.append(8 - desc.key.length(), ' ');
		descline.append(desc.cmd);
		descline.append(24 - desc.cmd.length(), ' ');
		descline.append(desc.desc);
		line.append(Stfl::quote(descline));

		line.append("}");

		code.append(line);
	}

	code.append("}");

	help_form.modify("helptext", "replace_inner", code);

	bool quit = false;

	do {
		const char* event = help_form.run(0);
		if (!event) {
			continue;
		}

		Operation op = keys->get_operation(event, "help");

		switch (op) {
		case OP_HARDQUIT:
		case OP_QUIT:
			quit = true;
			break;
		default:
			break;
		}
	} while (!quit);
}

std::string PbView::prepare_keymaphint(KeyMapHintEntry* hints)
{
	std::string keymap_hint;
	for (int i = 0; hints[i].op != OP_NIL; ++i) {
		keymap_hint.append(keys->getkey(hints[i].op, "podbeuter"));
		keymap_hint.append(":");
		keymap_hint.append(hints[i].text);
		keymap_hint.append(" ");
	}
	return keymap_hint;
}

void PbView::set_help_keymap_hint()
{
	KeyMapHintEntry hints[] = {{OP_QUIT, _("Quit")}, {OP_NIL, nullptr}};
	std::string keymap_hint = prepare_keymaphint(hints);
	help_form.set("help", keymap_hint);
}

void PbView::set_dllist_keymap_hint()
{
	KeyMapHintEntry hints[] = {{OP_QUIT, _("Quit")},
		{OP_PB_DOWNLOAD, _("Download")},
		{OP_PB_CANCEL, _("Cancel")},
		{OP_PB_DELETE, _("Delete")},
		{OP_PB_PURGE, _("Purge Finished")},
		{OP_PB_TOGGLE_DLALL, _("Toggle Automatic Download")},
		{OP_PB_PLAY, _("Play")},
		{OP_PB_MARK_FINISHED, _("Mark as Finished")},
		{OP_HELP, _("Help")},
		{OP_NIL, nullptr}
	};

	std::string keymap_hint = prepare_keymaphint(hints);
	dllist_form.set("help", keymap_hint);
}

std::string PbView::format_line(const std::string& podlist_format,
	const Download& dl,
	unsigned int pos,
	unsigned int width)
{
	FmtStrFormatter fmt;

	const double speed_kbps = dl.kbps();
	const auto speed = get_speed_human_readable(speed_kbps);

	fmt.register_fmt('i', strprintf::fmt("%u", pos + 1));
	fmt.register_fmt('d',
		strprintf::fmt("%.1f", dl.current_size() / (1024 * 1024)));
	fmt.register_fmt(
		't', strprintf::fmt("%.1f", dl.total_size() / (1024 * 1024)));
	fmt.register_fmt('p', strprintf::fmt("%.1f", dl.percents_finished()));
	fmt.register_fmt('k', strprintf::fmt("%.2f", speed_kbps));
	fmt.register_fmt('K', strprintf::fmt("%.2f %s", speed.first, speed.second));
	fmt.register_fmt('S', strprintf::fmt("%s", dl.status_text()));
	fmt.register_fmt('u', strprintf::fmt("%s", dl.url()));
	fmt.register_fmt('F', strprintf::fmt("%s", dl.filename()));
	fmt.register_fmt('b', strprintf::fmt("%s", dl.basename()));

	auto formattedLine = fmt.do_format(podlist_format, width);
	return formattedLine;
}

} // namespace podboat
<|endoftext|>