<commit_before>#include "apfMPAS.h"
#include <apfMesh2.h>
#include <apfNumbering.h>
#include <diffMC/src/parma_ghostOwner.h>
#include <netcdf>

namespace apf {

using namespace netCDF;

struct MpasFile
{
  ~MpasFile()
  {
    delete [] x;
    delete [] y;
    delete [] z;
    delete [] conn;
  }
  int elements;
  int nodes;
  int nodesPerElement;
  double* x;
  double* y;
  double* z;
/* conn[i * vertexDegree + j] - 1
   is the index of the j'th node
   of element i */
  int* conn;
};

static int readDim(NcFile& f, const char* name)
{
  return f.getDim(name).getSize();
}

static double* readDoubles(NcFile& f, const char* name, int n)
{
  double* a = new double[n];
  NcVar v = f.getVar(name);
  v.getVar(a);
  return a;
}

static int* readInts(NcFile& f, const char* name, int n)
{
  int* a = new int[n];
  NcVar v = f.getVar(name);
  v.getVar(a);
  return a;
}

void readMpasFile(MpasFile& out, const char* filename)
{
  NcFile in(filename, NcFile::read);
  /* this is the dual of a hexagonal mesh,
     hence the reversing of terms */
  out.nodes = readDim(in, "nCells");
  printf("%d nodes\n", out.nodes);
  out.elements = readDim(in, "nVertices");
  printf("%d elements\n", out.elements);
  out.nodesPerElement = readDim(in, "vertexDegree");
  out.x = readDoubles(in, "xCell", out.nodes);
  out.y = readDoubles(in, "yCell", out.nodes);
  out.z = readDoubles(in, "zCell", out.nodes);
  out.conn = readInts(in, "cellsOnVertex", out.elements * out.nodesPerElement);
}

static int getType(int nodesPerElement)
{
  if (nodesPerElement == 3)
    return apf::Mesh::TRIANGLE;
  if (nodesPerElement == 4)
    return apf::Mesh::QUAD;
  abort();
}

void numberInitialNodes(MpasFile& in, apf::Mesh2* out,
    apf::MeshEntity** v)
{
  apf::Numbering* n = apf::createNumbering(out, "mpas_id", out->getShape(), 1);
  for (int i = 0; i < in.nodes; ++i)
    apf::number(n, v[i], 0, 0, i);
}

void buildInitialMesh(MpasFile& in, apf::Mesh2* out)
{
  apf::MeshEntity** v = new apf::MeshEntity* [in.nodes];
/* fake classification for now, deriveMdsModel will fix this up */
  apf::ModelEntity* c = out->findModelEntity(2, 0);
  int t = getType(in.nodesPerElement);
  for (int i = 0; i < in.nodes; ++i)
    v[i] = out->createVert(c);
  for (int i = 0; i < in.nodes; ++i) {
    apf::Vector3 x(in.x[i], in.y[i], in.z[i]);
    out->setPoint(v[i], 0, x);
  }
  for (int i = 0; i < in.elements; ++i) {
    apf::Downward ev;
    for (int j = 0; j < in.nodesPerElement; ++j) {
      int vi = in.conn[i * in.nodesPerElement + j] - 1;
/* zero indices (-1 after conversion) indicate that an MPAS
   vertex does not have an adjacent cell in that direction
   (it is a boundary vertex). In our dual mesh, we simply
   omit that dual element. This is correct and should not
   cause problems. */
      if (vi < 0)
        goto bad_vi;
      assert(vi < in.nodes);
      ev[j] = v[vi];
    }
    apf::buildElement(out, c, t, ev);
bad_vi:
    continue;
  }
  numberInitialNodes(in, out, v);
  delete [] v;
}

void removeIsolatedNodes(apf::Mesh2* m)
{
/* another problem with the dual: MPAS cells without adjacent
   cells become vertices with no adjacent elements in the
   dual mesh, which our codes don't handle.
   We have to give up on these cells, but tell the user about it.
   We also need to be aware of these omitted cells going forward */
  apf::MeshIterator* it = m->begin(0);
  int n = 0;
  apf::MeshEntity* e;
  while ((e = m->iterate(it)))
    if ( ! m->countUpward(e)) {
      m->destroy(e);
      ++n;
    }
  m->end(it);
  if (n)
    fprintf(stderr,
        "warning: removed %d isolated nodes from MPAS dual mesh\n",
        n);
}

void loadMpasMesh(apf::Mesh2* m, const char* filename)
{
  MpasFile f;
  readMpasFile(f, filename);
  buildInitialMesh(f, m);
  removeIsolatedNodes(m);
}

void writeMpasAssignments(apf::Mesh2* m, const char* filename) {
  NcFile in(filename, NcFile::read);
  /* this is the dual of a hexagonal mesh,
     hence the reversing of terms */
  int numMpasVtx = readDim(in, "nCells");

  apf::Numbering* n = apf::createNumbering(m, "mpas_id", m->getShape(), 1);

  /* collect N/#parts contiguous vertex assignments on each process
     (and deal with any remainders) */
  int numPerPart = numMpasVtx / PCU_Comm_Peers() + 1;
  apf::MeshIterator* itr = m->begin(0);
  apf::MeshEntity* e;
  int size;
  if (PCU_Comm_Self()==PCU_Comm_Peers()-1)
    size=numMpasVtx%numPerPart;
  else
    size=numPerPart;
  int* vtxs = new int[size];
  for (int i=0;i<size;i++)
    vtxs[i]=-1;
  PCU_Comm_Begin();
  while ((e = m->iterate(itr))) {
    if (!parma::isOwned(m, e))
      continue;
    int num = getNumber(n, e, 0, 0);
    int target = num / numPerPart;
    if (target == PCU_Comm_Self())
      vtxs[num%numPerPart] = PCU_Comm_Self();
    else
      PCU_COMM_PACK(target,num);
  }
  m->end(itr);

  PCU_Comm_Send();
  while (PCU_Comm_Listen()) {
    int owner =PCU_Comm_Sender();
    while (!PCU_Comm_Unpacked()) {
      int num;
      PCU_COMM_UNPACK(num);
      vtxs[num%numPerPart]=owner;
    }
  }
  // assign missing vertices to a random part id
  int count = 0;
  for (int i = 0;
       (i < numPerPart) && (i+numPerPart*PCU_Comm_Self() < numMpasVtx);
       i++)
    if (vtxs[i]==-1) {
      vtxs[i] = 0; //to be random
      count++;
    }
  fprintf(stdout,"missing vertices found %d\n",count);
  // use MPI IO to write the contiguous blocks to a single graph.info.part.<#parts> file
  // see https://gist.github.com/cwsmith/166d5beb400f3a8136f7 and the comments

  delete [] vtxs;
}

}
<commit_msg>changed array to vector<commit_after>#include "apfMPAS.h"
#include <apfMesh2.h>
#include <apfNumbering.h>
#include <diffMC/src/parma_ghostOwner.h>
#include <netcdf>

namespace apf {

using namespace netCDF;

struct MpasFile
{
  ~MpasFile()
  {
    delete [] x;
    delete [] y;
    delete [] z;
    delete [] conn;
  }
  int elements;
  int nodes;
  int nodesPerElement;
  double* x;
  double* y;
  double* z;
/* conn[i * vertexDegree + j] - 1
   is the index of the j'th node
   of element i */
  int* conn;
};

static int readDim(NcFile& f, const char* name)
{
  return f.getDim(name).getSize();
}

static double* readDoubles(NcFile& f, const char* name, int n)
{
  double* a = new double[n];
  NcVar v = f.getVar(name);
  v.getVar(a);
  return a;
}

static int* readInts(NcFile& f, const char* name, int n)
{
  int* a = new int[n];
  NcVar v = f.getVar(name);
  v.getVar(a);
  return a;
}

void readMpasFile(MpasFile& out, const char* filename)
{
  NcFile in(filename, NcFile::read);
  /* this is the dual of a hexagonal mesh,
     hence the reversing of terms */
  out.nodes = readDim(in, "nCells");
  printf("%d nodes\n", out.nodes);
  out.elements = readDim(in, "nVertices");
  printf("%d elements\n", out.elements);
  out.nodesPerElement = readDim(in, "vertexDegree");
  out.x = readDoubles(in, "xCell", out.nodes);
  out.y = readDoubles(in, "yCell", out.nodes);
  out.z = readDoubles(in, "zCell", out.nodes);
  out.conn = readInts(in, "cellsOnVertex", out.elements * out.nodesPerElement);
}

static int getType(int nodesPerElement)
{
  if (nodesPerElement == 3)
    return apf::Mesh::TRIANGLE;
  if (nodesPerElement == 4)
    return apf::Mesh::QUAD;
  abort();
}

void numberInitialNodes(MpasFile& in, apf::Mesh2* out,
    apf::MeshEntity** v)
{
  apf::Numbering* n = apf::createNumbering(out, "mpas_id", out->getShape(), 1);
  for (int i = 0; i < in.nodes; ++i)
    apf::number(n, v[i], 0, 0, i);
}

void buildInitialMesh(MpasFile& in, apf::Mesh2* out)
{
  apf::MeshEntity** v = new apf::MeshEntity* [in.nodes];
/* fake classification for now, deriveMdsModel will fix this up */
  apf::ModelEntity* c = out->findModelEntity(2, 0);
  int t = getType(in.nodesPerElement);
  for (int i = 0; i < in.nodes; ++i)
    v[i] = out->createVert(c);
  for (int i = 0; i < in.nodes; ++i) {
    apf::Vector3 x(in.x[i], in.y[i], in.z[i]);
    out->setPoint(v[i], 0, x);
  }
  for (int i = 0; i < in.elements; ++i) {
    apf::Downward ev;
    for (int j = 0; j < in.nodesPerElement; ++j) {
      int vi = in.conn[i * in.nodesPerElement + j] - 1;
/* zero indices (-1 after conversion) indicate that an MPAS
   vertex does not have an adjacent cell in that direction
   (it is a boundary vertex). In our dual mesh, we simply
   omit that dual element. This is correct and should not
   cause problems. */
      if (vi < 0)
        goto bad_vi;
      assert(vi < in.nodes);
      ev[j] = v[vi];
    }
    apf::buildElement(out, c, t, ev);
bad_vi:
    continue;
  }
  numberInitialNodes(in, out, v);
  delete [] v;
}

void removeIsolatedNodes(apf::Mesh2* m)
{
/* another problem with the dual: MPAS cells without adjacent
   cells become vertices with no adjacent elements in the
   dual mesh, which our codes don't handle.
   We have to give up on these cells, but tell the user about it.
   We also need to be aware of these omitted cells going forward */
  apf::MeshIterator* it = m->begin(0);
  int n = 0;
  apf::MeshEntity* e;
  while ((e = m->iterate(it)))
    if ( ! m->countUpward(e)) {
      m->destroy(e);
      ++n;
    }
  m->end(it);
  if (n)
    fprintf(stderr,
        "warning: removed %d isolated nodes from MPAS dual mesh\n",
        n);
}

void loadMpasMesh(apf::Mesh2* m, const char* filename)
{
  MpasFile f;
  readMpasFile(f, filename);
  buildInitialMesh(f, m);
  removeIsolatedNodes(m);
}

void writeMpasAssignments(apf::Mesh2* m, const char* filename) {
  NcFile in(filename, NcFile::read);
  /* this is the dual of a hexagonal mesh,
     hence the reversing of terms */
  int numMpasVtx = readDim(in, "nCells");

  apf::Numbering* n = apf::createNumbering(m, "mpas_id", m->getShape(), 1);

  /* collect N/#parts contiguous vertex assignments on each process
     (and deal with any remainders) */
  int numPerPart = numMpasVtx / PCU_Comm_Peers() + 1;
  apf::MeshIterator* itr = m->begin(0);
  apf::MeshEntity* e;
  int size;
  if (PCU_Comm_Self()==PCU_Comm_Peers()-1)
    size=numMpasVtx%numPerPart;
  else
    size=numPerPart;
  std::vector<int> vtxs(size,-1);
  PCU_Comm_Begin();
  while ((e = m->iterate(itr))) {
    if (!parma::isOwned(m, e))
      continue;
    int num = getNumber(n, e, 0, 0);
    int target = num / numPerPart;
    if (target == PCU_Comm_Self())
      vtxs[num%numPerPart] = PCU_Comm_Self();
    else
      PCU_COMM_PACK(target,num);
  }
  m->end(itr);

  PCU_Comm_Send();
  while (PCU_Comm_Listen()) {
    int owner =PCU_Comm_Sender();
    while (!PCU_Comm_Unpacked()) {
      int num;
      PCU_COMM_UNPACK(num);
      vtxs[num%numPerPart]=owner;
    }
  }
  // assign missing vertices to a random part id
  int count = 0;
  for (int i = 0;
       (i < numPerPart) && (i+numPerPart*PCU_Comm_Self() < numMpasVtx);
       i++)
    if (vtxs[i]==-1) {
      vtxs[i] = 0; //to be random
      count++;
    }
  fprintf(stdout,"missing vertices found %d\n",count);
  // use MPI IO to write the contiguous blocks to a single graph.info.part.<#parts> file
  // see https://gist.github.com/cwsmith/166d5beb400f3a8136f7 and the comments
}

}
<|endoftext|>


<commit_before><commit_msg>Resolves: fdo#39348 toggling grammar checkbox deletes sentence<commit_after><|endoftext|>


<commit_before>#pragma once
/*
**  Copyright (C) 2013 Aldebaran Robotics
**  See COPYING for the license
*/

#ifndef _QI_TYPE_DETAIL_ANYREFERENCE_HPP_
#define _QI_TYPE_DETAIL_ANYREFERENCE_HPP_

#include <vector>
#include <map>
#include <stdexcept>
#include <cmath>
#include <qi/atomic.hpp>
#include <qi/types.hpp>
#include <qi/api.hpp>
#include <qi/type/fwd.hpp>
#include <qi/type/detail/typeinterface.hpp>
#include <boost/type_traits/remove_const.hpp>

namespace qi {

class AnyReference;
using AnyReferenceVector = std::vector<AnyReference>;

namespace detail {

/** Class that holds any value, with informations to manipulate it.
 *  operator=() makes a shallow copy.
 *
 * \warning AnyReference should not be used directly as call arguments.
 * Use qi::AnyValue which has value semantics instead.
 *
 */
class QI_API AnyReferenceBase
{
protected:
  AnyReferenceBase();

  ///@{
  /// Low level Internal API

  /** Store type and allocate storage of value.
   * @param type use this type for initialization
   */
  explicit AnyReferenceBase(TypeInterface* type);

  /** Create a generic value with type and a value who should have
   * already been allocated.
   * @param type type of this generic value
   * @param value an already alloc place to store value
   */
  AnyReferenceBase(TypeInterface* type, void* value)
    : _type(type)
    , _value(value)
  {}

public:
  /// @return the pair (convertedValue, trueIfCopiedAndNeedsDestroy)
  std::pair<AnyReference, bool> convert(TypeInterface* targetType) const;
  std::pair<AnyReference, bool> convert(ListTypeInterface* targetType) const;
  std::pair<AnyReference, bool> convert(StructTypeInterface* targetType) const;
  std::pair<AnyReference, bool> convert(MapTypeInterface* targetType) const;
  std::pair<AnyReference, bool> convert(IntTypeInterface* targetType) const;
  std::pair<AnyReference, bool> convert(FloatTypeInterface* targetType) const;
  std::pair<AnyReference, bool> convert(RawTypeInterface* targetType) const;
  std::pair<AnyReference, bool> convert(StringTypeInterface* targetType) const;
  std::pair<AnyReference, bool> convert(PointerTypeInterface* targetType) const;
  std::pair<AnyReference, bool> convert(DynamicTypeInterface* targetType) const;

  /** Return the typed pointer behind a AnyReference. T *must* be the type
   * of the value.
   * @return a pointer to the value as a T or 0 if value is not a T.
   * @param check if false, does not validate type before converting
   */
  template<typename T>
  T* ptr(bool check = true);

  bool isValid() const;
  /// @return true if value is valid and not void
  bool isValue() const;

  /// Helper function that converts and always clone
  AnyReference convertCopy(TypeInterface* targetType) const;

  // get item with key/index 'key'. Return empty GVP or throw in case of failure
  AnyReference _element(const AnyReference& key, bool throwOnFailure);
  ///@}

  ///@{
  /** Construction and assign.
   */

  /** Construct a AnyValue with storage pointing to ptr.
   * @warning the AnyReference will become invalid if ptr
   * is destroyed (if it gets deleted or points to the stack and goes
   * out of scope).
   */
  template <typename T>
  static AnyReference from(const T& ref);

  template <typename T>
  static AnyReference fromPtr(const T* ptr);

  AnyReference clone() const;
  /// Deletes storage.
  void destroy();
  ///@}

  ///@{
  /** The following methods return a typed copy of the stored value,
   * converting if necessary.
   * They throw in case of conversion failure.
   */

  /// Convert to anything or throw trying.
  template<typename T>
  T to() const;

  /// Similar to previous method, but uses a dummy value to get the target type
  template<typename T>
  T to(const T&) const;

  int64_t      toInt()    const;
  uint64_t     toUInt()   const;
  float        toFloat()  const;
  double       toDouble() const;
  std::string  toString() const;

  template<typename T>
  std::vector<T> toList() const;

  template<typename K, typename V>
  std::map<K, V> toMap() const;

  AnyObject  toObject() const;

  /** Convert the value to a tuple.
   * If value is currently a tuple, it will be returned.
   * If value is a list its elements will become the tuple components.
   * @param homogeneous if true, all tuple elements will be of the type
   * of the list element type. If false, the effective type of elements
   * of kind dynamic will be used.
   */
  AnyValue toTuple(bool homogeneous) const;
  ///@}

  qi::Signature signature(bool resolveDynamic = false) const;
  TypeKind kind() const;

  ///@{
  /** Read and update functions
   *  The following functions access or modify the existing value.
   *  They never change the storage location or type.
   *  They will fail by throwing an exception if the requested operation
   * is incompatible with the current value type.
   *
   * @warning a AnyReference refering to a container element will
   * become invalid as soon as the container is modified.
   *
   */

  /** @return a typed reference to the underlying value
   *  @warning This method will only succeed if T exactly matches
   *  the type of the value stored. No conversion will be performed.
   *  So if you only want a value and not a reference, use to() instead.
   */
  template<typename T>
  T& as();

  int64_t&     asInt64()  { return as<int64_t>();}
  uint64_t&    asUInt64() { return as<uint64_t>();}
  int32_t&     asInt32()  { return as<int32_t>();}
  uint32_t&    asUInt32() { return as<uint32_t>();}
  int16_t&     asInt16()  { return as<int16_t>();}
  uint16_t&    asUInt16() { return as<uint16_t>();}
  int8_t&      asInt8()   { return as<int8_t>();}
  uint8_t&     asUInt8()  { return as<uint8_t>();}
  double&      asDouble() { return as<double>();}
  float&       asFloat()  { return as<float>();}
  std::string& asString() { return as<std::string>();}

  /// @return a pair of (char*, size) corresponding to the raw buffer. No copy made.
  std::pair<char*, size_t> asRaw() const;

  /** @return contained AnyValue or throw if type is not dynamic.
   * @note Returned AnyReference might be empty.
   */
  AnyReference content() const;

  /// @{
  /** Container partial unboxing.
   * The following functions unbox the container-part of the value.
   * The values in the contairer are exposed as AnyReference.
   * The values can be modified using the set and as function families,
   * But the container itself is a copy.
   * @warning for better performances use the begin() and end() iterator API
   */
  AnyReferenceVector asTupleValuePtr();
  AnyReferenceVector asListValuePtr();
  std::map<AnyReference, AnyReference> asMapValuePtr();
  /// @}

  ///TODO: update == set  (remove one)
  /// Update the value with the one in b
  void update(const AutoAnyReference& b);

  /// Update the value to val, which will be converted if required.
  template<typename T>
  void set(const T& val);

  void set(int64_t v)            { setInt(v); }
  void set(int32_t v)            { setInt(v); }
  void set(uint64_t v)           { setUInt(v); }
  void set(uint32_t v)           { setUInt(v); }
  void set(float v)              { setFloat(v); }
  void set(double v)             { setDouble(v); }
  void set(const std::string& v) { setString(v); }

  void  setInt(int64_t v);
  void  setUInt(uint64_t v);
  void  setFloat(float v);
  void  setDouble(double v);
  void  setString(const std::string& v);
  void  setDynamic(const AnyReference &value);

  /// set the value of the raw buffer, a copy will be made.
  /// @throw std::runtime_error when kind is not Raw
  void  setRaw(const char *buffer, size_t size);

  /// set the values of the tuple. A copy will be made.
  /// @throw std::runtime_error when kind is not Tuple
  void  setTuple(const AnyReferenceVector& values);

  ///@{
  /// In-place container manipulation.

  /** Return a reference to container element at index or key idx.
   *  Use set methods on the result for inplace modification.
   *  Behavior depends on the container kind:
   *  - List or tuple: The key must be of integral type. Boundary checks
   *    are performed.
   *  - Map: The key must be of a convertible type to the container key type.
   *    If the key is not found in the container, a new default-valued
   *    Element will be created, inserted. and returned.
   *  @warning the returned value is only valid until owning container
   *  is changed.
   */
  template<typename K>
  AnyReference operator[](const K& key);

  /// Call operator[](key).as<E>, element type must match E
  template<typename E, typename K>
  E& element(const K& key);

  /**
   * Similar to operator[], but Map container type is not modified if
   * the key does not exist.
   * Returns an empty AnyReference if the key is invalid (out of bounds for
   * list/tuple or key not found for the map)
   */
  template<typename K>
  AnyReference at(const K& key);
  template<typename K>
  AnyReference at(const K& key) const;
  AnyReference at(const AnyReference& key);
  AnyReference at(const AnyReference& key) const;

  size_t size() const;

  //TODO: use AutoAnyReference
  template<typename T>
  void append(const T& element);
  void append(const AnyReference& element);

  template<typename K, typename V>
  void insert(const K& key, const V& val);
  void insert(const AnyReference& key, const AnyReference& val);

  /** Similar to operator[](), but return an empty AnyValue
   * If the key is not present.
   */
  template<typename K>
  AnyReference find(const K& key);

  /// Return an iterator on the beginning of the container
  AnyIterator begin() const; //we lie on const but GV does not honor it yet
  /// Return an iterator on the end of the container
  AnyIterator end() const;

  /// Dereference pointer, iterator or dynamic
  AnyReference operator*() const;
  ///@}

  ///@}

  TypeInterface* type() const { return _type; }
  /// @return list of tuple elements type, or throw if not a tuple
  std::vector<TypeInterface*> membersType() const;
  void* rawValue() const { return _value; }

protected:
  TypeInterface* _type;
  void*          _value;
};

} // namespace detail

class QI_API AnyReference : public detail::AnyReferenceBase {
public:
  AnyReference()
    : AnyReferenceBase()
  {}

  AnyReference(const AnyReferenceBase& rhs)
    : AnyReferenceBase(rhs)
  {}

  explicit AnyReference(TypeInterface* type)
    : AnyReferenceBase(type)
  {}

  AnyReference(TypeInterface* type, void* value)
    : AnyReferenceBase(type, value)
  {}

private:
  //block the dangerous automatic cast from AnyValue to AnyRef
  //use AnyValue::asReference() to take a ref on the content of a value.
  //use AnyValue::from(value) to take a ref on the value.
  AnyReference(const AnyValue& val)
  {
    throw std::runtime_error("invalid internal operation.");
  };
};

using AnyReferenceVector = std::vector<AnyReference>;
QI_API bool operator< (const AnyReference& a, const AnyReference& b);
QI_API bool operator==(const AnyReference& a, const AnyReference& b);
QI_API bool operator!=(const AnyReference& a, const AnyReference& b);

///@return an allocated Tuple made from copies of \param values
QI_API AnyReference makeGenericTuple(const AnyReferenceVector& values);

///@return a Tuple pointing to values as its storage
QI_API AnyReference makeGenericTuplePtr(
    const std::vector<TypeInterface*>& types,
    const std::vector<void*>& values);

/** Generates AnyReference from everything transparently.
 * To be used as type of meta-function call argument
 *
 *  Example:
 *    void metaCall(ValueGen arg1, ValueGen arg2);
 *  can be called with any argument type:
 *    metaCall("foo", 12);
 */
class QI_API AutoAnyReference: public AnyReference
{
public:
  AutoAnyReference () {}
  AutoAnyReference(const AutoAnyReference& b)
    : AnyReference()
  {
    _value = b._value;
    _type = b._type;
  }

  AutoAnyReference(const AnyReference &self)
    : AnyReference(self)
  {}

  AutoAnyReference(const detail::AnyReferenceBase &self)
    : AnyReference(self)
  {}

  template<typename T>
  AutoAnyReference(const T& ptr)
  {
    *(AnyReference*)this = AnyReference::from(ptr);
  }
};

} // namespace qi

#include <qi/type/detail/anyreference.hxx>

#include <qi/type/typeinterface.hpp>

/* Since AnyReference does not handle its memory, it cannot be used
 * inside a AnyReference. use AnyValue instead.
 */
QI_NO_TYPE(qi::AnyReference);
QI_NO_TYPE(qi::AutoAnyReference);

#endif
<commit_msg>Move AnyReferenceBase::_element() function to protected part of the class.<commit_after>#pragma once
/*
**  Copyright (C) 2013 Aldebaran Robotics
**  See COPYING for the license
*/

#ifndef _QI_TYPE_DETAIL_ANYREFERENCE_HPP_
#define _QI_TYPE_DETAIL_ANYREFERENCE_HPP_

#include <vector>
#include <map>
#include <stdexcept>
#include <cmath>
#include <qi/atomic.hpp>
#include <qi/types.hpp>
#include <qi/api.hpp>
#include <qi/type/fwd.hpp>
#include <qi/type/detail/typeinterface.hpp>
#include <boost/type_traits/remove_const.hpp>

namespace qi {

class AnyReference;
using AnyReferenceVector = std::vector<AnyReference>;

namespace detail {

/** Class that holds any value, with informations to manipulate it.
 *  operator=() makes a shallow copy.
 *
 * \warning AnyReference should not be used directly as call arguments.
 * Use qi::AnyValue which has value semantics instead.
 *
 */
class QI_API AnyReferenceBase
{
protected:
  AnyReferenceBase();

  ///@{
  /// Low level Internal API

  /** Store type and allocate storage of value.
   * @param type use this type for initialization
   */
  explicit AnyReferenceBase(TypeInterface* type);

  /** Create a generic value with type and a value who should have
   * already been allocated.
   * @param type type of this generic value
   * @param value an already alloc place to store value
   */
  AnyReferenceBase(TypeInterface* type, void* value)
    : _type(type)
    , _value(value)
  {}

  // get item with key/index 'key'. Return empty GVP or throw in case of failure
  AnyReference _element(const AnyReference& key, bool throwOnFailure);

public:
  /// @return the pair (convertedValue, trueIfCopiedAndNeedsDestroy)
  std::pair<AnyReference, bool> convert(TypeInterface* targetType) const;
  std::pair<AnyReference, bool> convert(ListTypeInterface* targetType) const;
  std::pair<AnyReference, bool> convert(StructTypeInterface* targetType) const;
  std::pair<AnyReference, bool> convert(MapTypeInterface* targetType) const;
  std::pair<AnyReference, bool> convert(IntTypeInterface* targetType) const;
  std::pair<AnyReference, bool> convert(FloatTypeInterface* targetType) const;
  std::pair<AnyReference, bool> convert(RawTypeInterface* targetType) const;
  std::pair<AnyReference, bool> convert(StringTypeInterface* targetType) const;
  std::pair<AnyReference, bool> convert(PointerTypeInterface* targetType) const;
  std::pair<AnyReference, bool> convert(DynamicTypeInterface* targetType) const;

  /** Return the typed pointer behind a AnyReference. T *must* be the type
   * of the value.
   * @return a pointer to the value as a T or 0 if value is not a T.
   * @param check if false, does not validate type before converting
   */
  template<typename T>
  T* ptr(bool check = true);

  bool isValid() const;
  /// @return true if value is valid and not void
  bool isValue() const;

  /// Helper function that converts and always clone
  AnyReference convertCopy(TypeInterface* targetType) const;
  ///@}

  ///@{
  /** Construction and assign.
   */

  /** Construct a AnyValue with storage pointing to ptr.
   * @warning the AnyReference will become invalid if ptr
   * is destroyed (if it gets deleted or points to the stack and goes
   * out of scope).
   */
  template <typename T>
  static AnyReference from(const T& ref);

  template <typename T>
  static AnyReference fromPtr(const T* ptr);

  AnyReference clone() const;
  /// Deletes storage.
  void destroy();
  ///@}

  ///@{
  /** The following methods return a typed copy of the stored value,
   * converting if necessary.
   * They throw in case of conversion failure.
   */

  /// Convert to anything or throw trying.
  template<typename T>
  T to() const;

  /// Similar to previous method, but uses a dummy value to get the target type
  template<typename T>
  T to(const T&) const;

  int64_t      toInt()    const;
  uint64_t     toUInt()   const;
  float        toFloat()  const;
  double       toDouble() const;
  std::string  toString() const;

  template<typename T>
  std::vector<T> toList() const;

  template<typename K, typename V>
  std::map<K, V> toMap() const;

  AnyObject  toObject() const;

  /** Convert the value to a tuple.
   * If value is currently a tuple, it will be returned.
   * If value is a list its elements will become the tuple components.
   * @param homogeneous if true, all tuple elements will be of the type
   * of the list element type. If false, the effective type of elements
   * of kind dynamic will be used.
   */
  AnyValue toTuple(bool homogeneous) const;
  ///@}

  qi::Signature signature(bool resolveDynamic = false) const;
  TypeKind kind() const;

  ///@{
  /** Read and update functions
   *  The following functions access or modify the existing value.
   *  They never change the storage location or type.
   *  They will fail by throwing an exception if the requested operation
   * is incompatible with the current value type.
   *
   * @warning a AnyReference refering to a container element will
   * become invalid as soon as the container is modified.
   *
   */

  /** @return a typed reference to the underlying value
   *  @warning This method will only succeed if T exactly matches
   *  the type of the value stored. No conversion will be performed.
   *  So if you only want a value and not a reference, use to() instead.
   */
  template<typename T>
  T& as();

  int64_t&     asInt64()  { return as<int64_t>();}
  uint64_t&    asUInt64() { return as<uint64_t>();}
  int32_t&     asInt32()  { return as<int32_t>();}
  uint32_t&    asUInt32() { return as<uint32_t>();}
  int16_t&     asInt16()  { return as<int16_t>();}
  uint16_t&    asUInt16() { return as<uint16_t>();}
  int8_t&      asInt8()   { return as<int8_t>();}
  uint8_t&     asUInt8()  { return as<uint8_t>();}
  double&      asDouble() { return as<double>();}
  float&       asFloat()  { return as<float>();}
  std::string& asString() { return as<std::string>();}

  /// @return a pair of (char*, size) corresponding to the raw buffer. No copy made.
  std::pair<char*, size_t> asRaw() const;

  /** @return contained AnyValue or throw if type is not dynamic.
   * @note Returned AnyReference might be empty.
   */
  AnyReference content() const;

  /// @{
  /** Container partial unboxing.
   * The following functions unbox the container-part of the value.
   * The values in the contairer are exposed as AnyReference.
   * The values can be modified using the set and as function families,
   * But the container itself is a copy.
   * @warning for better performances use the begin() and end() iterator API
   */
  AnyReferenceVector asTupleValuePtr();
  AnyReferenceVector asListValuePtr();
  std::map<AnyReference, AnyReference> asMapValuePtr();
  /// @}

  ///TODO: update == set  (remove one)
  /// Update the value with the one in b
  void update(const AutoAnyReference& b);

  /// Update the value to val, which will be converted if required.
  template<typename T>
  void set(const T& val);

  void set(int64_t v)            { setInt(v); }
  void set(int32_t v)            { setInt(v); }
  void set(uint64_t v)           { setUInt(v); }
  void set(uint32_t v)           { setUInt(v); }
  void set(float v)              { setFloat(v); }
  void set(double v)             { setDouble(v); }
  void set(const std::string& v) { setString(v); }

  void  setInt(int64_t v);
  void  setUInt(uint64_t v);
  void  setFloat(float v);
  void  setDouble(double v);
  void  setString(const std::string& v);
  void  setDynamic(const AnyReference &value);

  /// set the value of the raw buffer, a copy will be made.
  /// @throw std::runtime_error when kind is not Raw
  void  setRaw(const char *buffer, size_t size);

  /// set the values of the tuple. A copy will be made.
  /// @throw std::runtime_error when kind is not Tuple
  void  setTuple(const AnyReferenceVector& values);

  ///@{
  /// In-place container manipulation.

  /** Return a reference to container element at index or key idx.
   *  Use set methods on the result for inplace modification.
   *  Behavior depends on the container kind:
   *  - List or tuple: The key must be of integral type. Boundary checks
   *    are performed.
   *  - Map: The key must be of a convertible type to the container key type.
   *    If the key is not found in the container, a new default-valued
   *    Element will be created, inserted. and returned.
   *  @warning the returned value is only valid until owning container
   *  is changed.
   */
  template<typename K>
  AnyReference operator[](const K& key);

  /// Call operator[](key).as<E>, element type must match E
  template<typename E, typename K>
  E& element(const K& key);

  /**
   * Similar to operator[], but Map container type is not modified if
   * the key does not exist.
   * Returns an empty AnyReference if the key is invalid (out of bounds for
   * list/tuple or key not found for the map)
   */
  template<typename K>
  AnyReference at(const K& key);
  template<typename K>
  AnyReference at(const K& key) const;
  AnyReference at(const AnyReference& key);
  AnyReference at(const AnyReference& key) const;

  size_t size() const;

  //TODO: use AutoAnyReference
  template<typename T>
  void append(const T& element);
  void append(const AnyReference& element);

  template<typename K, typename V>
  void insert(const K& key, const V& val);
  void insert(const AnyReference& key, const AnyReference& val);

  /** Similar to operator[](), but return an empty AnyValue
   * If the key is not present.
   */
  template<typename K>
  AnyReference find(const K& key);

  /// Return an iterator on the beginning of the container
  AnyIterator begin() const; //we lie on const but GV does not honor it yet
  /// Return an iterator on the end of the container
  AnyIterator end() const;

  /// Dereference pointer, iterator or dynamic
  AnyReference operator*() const;
  ///@}

  ///@}

  TypeInterface* type() const { return _type; }
  /// @return list of tuple elements type, or throw if not a tuple
  std::vector<TypeInterface*> membersType() const;
  void* rawValue() const { return _value; }

protected:
  TypeInterface* _type;
  void*          _value;
};

} // namespace detail

class QI_API AnyReference : public detail::AnyReferenceBase {
public:
  AnyReference()
    : AnyReferenceBase()
  {}

  AnyReference(const AnyReferenceBase& rhs)
    : AnyReferenceBase(rhs)
  {}

  explicit AnyReference(TypeInterface* type)
    : AnyReferenceBase(type)
  {}

  AnyReference(TypeInterface* type, void* value)
    : AnyReferenceBase(type, value)
  {}

private:
  //block the dangerous automatic cast from AnyValue to AnyRef
  //use AnyValue::asReference() to take a ref on the content of a value.
  //use AnyValue::from(value) to take a ref on the value.
  AnyReference(const AnyValue& val)
  {
    throw std::runtime_error("invalid internal operation.");
  };
};

using AnyReferenceVector = std::vector<AnyReference>;
QI_API bool operator< (const AnyReference& a, const AnyReference& b);
QI_API bool operator==(const AnyReference& a, const AnyReference& b);
QI_API bool operator!=(const AnyReference& a, const AnyReference& b);

///@return an allocated Tuple made from copies of \param values
QI_API AnyReference makeGenericTuple(const AnyReferenceVector& values);

///@return a Tuple pointing to values as its storage
QI_API AnyReference makeGenericTuplePtr(
    const std::vector<TypeInterface*>& types,
    const std::vector<void*>& values);

/** Generates AnyReference from everything transparently.
 * To be used as type of meta-function call argument
 *
 *  Example:
 *    void metaCall(ValueGen arg1, ValueGen arg2);
 *  can be called with any argument type:
 *    metaCall("foo", 12);
 */
class QI_API AutoAnyReference: public AnyReference
{
public:
  AutoAnyReference () {}
  AutoAnyReference(const AutoAnyReference& b)
    : AnyReference()
  {
    _value = b._value;
    _type = b._type;
  }

  AutoAnyReference(const AnyReference &self)
    : AnyReference(self)
  {}

  AutoAnyReference(const detail::AnyReferenceBase &self)
    : AnyReference(self)
  {}

  template<typename T>
  AutoAnyReference(const T& ptr)
  {
    *(AnyReference*)this = AnyReference::from(ptr);
  }
};

} // namespace qi

#include <qi/type/detail/anyreference.hxx>

#include <qi/type/typeinterface.hpp>

/* Since AnyReference does not handle its memory, it cannot be used
 * inside a AnyReference. use AnyValue instead.
 */
QI_NO_TYPE(qi::AnyReference);
QI_NO_TYPE(qi::AutoAnyReference);

#endif
<|endoftext|>


<commit_before>#include <vector>
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <ctime>
#include <boost/rational.hpp>
#include <boost/lexical_cast.hpp>

#include <boost/timer.hpp>

#include "timsort.hpp"

using namespace gfx;

enum state_t {
    sorted, randomized, reversed
};

template <typename value_t>
static void bench(int const size, state_t const state) {
    std::cerr << "size\t" << size << std::endl;

    std::less<value_t> lt;

    std::vector<value_t> a;
    for(int i = 0; i < size; ++i) {
        a.push_back((i+1) * 10);
    }

    switch(state) {
    case randomized:
        std::random_shuffle(a.begin(), a.end());
        break;
    case reversed:
        std::stable_sort(a.begin(), a.end(), lt);
        std::reverse(a.begin(), a.end());
        break;
    case sorted:
        std::stable_sort(a.begin(), a.end(), lt);
        break;
    default:
        assert(!"not reached");
    }

    {
        std::vector<value_t> b(a);
        boost::timer t;

        for(int i = 0; i < 100; ++i) {
            std::copy(a.begin(), a.end(), b.begin());
            std::sort(b.begin(), b.end(), lt);
        }

        std::cerr << "std::sort        " << t.elapsed() << std::endl;
    }

    {
        std::vector<value_t> b(a);
        boost::timer t;

        for(int i = 0; i < 100; ++i) {
            std::copy(a.begin(), a.end(), b.begin());
            std::stable_sort(b.begin(), b.end(), lt);
        }

        std::cerr << "std::stable_sort " << t.elapsed() << std::endl;
    }

    {
        std::vector<value_t> b(a);
        boost::timer t;

        for(int i = 0; i < 100; ++i) {
            std::copy(a.begin(), a.end(), b.begin());
            timsort(b.begin(), b.end(), lt);
        }

        std::cerr << "timsort          " << t.elapsed() << std::endl;
    }
}

static void doit(int const n, state_t const state) {
    std::cerr << "[int]" << std::endl;
    bench<int>(n, state);

    std::cerr << "[boost::rational]" << std::endl;
    bench< boost::rational<long long> >(n, state);
}

int main(int argc, const char *argv[]) {
    const int N =  argc > 1
        ? boost::lexical_cast<int>(argv[1])
        : 100 * 1000;

    std::cerr << std::setprecision(6) << std::setiosflags(std::ios::fixed);

    std::srand(0);

    std::cerr << "RANDOMIZED SEQUENCE" << std::endl;
    doit(N, randomized);

    std::cerr << "REVERSED SEQUENCE" << std::endl;
    doit(N, reversed);

    std::cerr << "SORTED SEQUENCE" << std::endl;
    doit(N, sorted);
}
<commit_msg>use default less function in bench<commit_after>#include <vector>
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <ctime>
#include <boost/rational.hpp>
#include <boost/lexical_cast.hpp>

#include <boost/timer.hpp>

#include "timsort.hpp"

using namespace gfx;

enum state_t {
    sorted, randomized, reversed
};

template <typename value_t>
static void bench(int const size, state_t const state) {
    std::cerr << "size\t" << size << std::endl;

    std::vector<value_t> a;
    for(int i = 0; i < size; ++i) {
        a.push_back((i+1) * 10);
    }

    switch(state) {
    case randomized:
        std::random_shuffle(a.begin(), a.end());
        break;
    case reversed:
        std::stable_sort(a.begin(), a.end());
        std::reverse(a.begin(), a.end());
        break;
    case sorted:
        std::stable_sort(a.begin(), a.end());
        break;
    default:
        assert(!"not reached");
    }

    {
        std::vector<value_t> b(a);
        boost::timer t;

        for(int i = 0; i < 100; ++i) {
            std::copy(a.begin(), a.end(), b.begin());
            std::sort(b.begin(), b.end());
        }

        std::cerr << "std::sort        " << t.elapsed() << std::endl;
    }

    {
        std::vector<value_t> b(a);
        boost::timer t;

        for(int i = 0; i < 100; ++i) {
            std::copy(a.begin(), a.end(), b.begin());
            std::stable_sort(b.begin(), b.end());
        }

        std::cerr << "std::stable_sort " << t.elapsed() << std::endl;
    }

    {
        std::vector<value_t> b(a);
        boost::timer t;

        for(int i = 0; i < 100; ++i) {
            std::copy(a.begin(), a.end(), b.begin());
            timsort(b.begin(), b.end());
        }

        std::cerr << "timsort          " << t.elapsed() << std::endl;
    }
}

static void doit(int const n, state_t const state) {
    std::cerr << "[int]" << std::endl;
    bench<int>(n, state);

    std::cerr << "[boost::rational]" << std::endl;
    bench< boost::rational<long long> >(n, state);
}

int main(int argc, const char *argv[]) {
    const int N =  argc > 1
        ? boost::lexical_cast<int>(argv[1])
        : 100 * 1000;

    std::cerr << std::setprecision(6) << std::setiosflags(std::ios::fixed);

    std::srand(0);

    std::cerr << "RANDOMIZED SEQUENCE" << std::endl;
    doit(N, randomized);

    std::cerr << "REVERSED SEQUENCE" << std::endl;
    doit(N, reversed);

    std::cerr << "SORTED SEQUENCE" << std::endl;
    doit(N, sorted);
}
<|endoftext|>


<commit_before>//                      MFEM Example 33
//
// Compile with: make ex33
//
// Sample runs:  ex33 -m ../data/square-disc.mesh -alpha 0.33 -o 2
//               ex33 -m ../data/star.mesh -alpha 0.99 -o 3
//               ex33 -m ../data/inline-quad.mesh -alpha 0.2 -o 3
//               ex33 -m ../data/disc-nurbs.mesh -alpha 0.33 -o 3
//               ex33 -m ../data/l-shape.mesh -alpha 0.33 -o 3 -r 4
//
//
// Description:
//
//  In this example we solve the following fractional PDE with MFEM:
//
//    ( - Δ )^α u = f  in Ω,      u = 0  on ∂Ω,      0 < α < 1,
//
//  To solve this FPDE, we rely on a rational approximation [2] of the normal
//  linear operator A^{-α}, where A = - Δ (with associated homogenous
//  boundary conditions). Namely, we first approximate the operator
//
//    A^{-α} ≈ Σ_{i=0}^N c_i (A + d_i I)^{-1},      d_0 = 0,   d_i > 0,
//
//  where I is the L2-identity operator and the coefficients c_i and d_i
//  are generated offline to a prescribed accuracy in a pre-processing step.
//  We use the triple-A algorithm [1] to generate the rational approximation
//  that this partial fractional expansion derives from. We then solve N+1
//  independent integer-order PDEs,
//
//    A u_i + d_i u_i = c_i f  in Ω,      u_i = 0  on ∂Ω,      i=0,...,N,
//
//  using MFEM and sum u_i to arrive at an approximate solution of the FPDE
//
//    u ≈ Σ_{i=0}^N u_i.
//
//
// References:
//
// [1] Nakatsukasa, Y., Sète, O., & Trefethen, L. N. (2018). The AAA algorithm
//     for rational approximation. SIAM Journal on Scientific Computing, 40(3),
//     A1494-A1522.
//
// [2] Harizanov, S., Lazarov, R., Margenov, S., Marinov, P., & Pasciak, J.
//     (2020). Analysis of numerical methods for spectral fractional elliptic
//     equations based on the best uniform rational approximation. Journal of
//     Computational Physics, 408, 109285.
//

#include "mfem.hpp"
#include <fstream>
#include <iostream>
#include "ex33.hpp"

using namespace std;
using namespace mfem;

int main(int argc, char *argv[])
{
   // 1. Parse command-line options.
   const char *mesh_file = "../data/star.mesh";
   int order = 1;
   int num_refs = 3;
   bool visualization = true;
   double alpha = 0.5;

   OptionsParser args(argc, argv);
   args.AddOption(&mesh_file, "-m", "--mesh",
                  "Mesh file to use.");
   args.AddOption(&order, "-o", "--order",
                  "Finite element order (polynomial degree) or -1 for"
                  " isoparametric space.");
   args.AddOption(&num_refs, "-r", "--refs",
                  "Number of uniform refinements");
   args.AddOption(&alpha, "-alpha", "--alpha",
                  "Fractional exponent");
   args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
                  "--no-visualization",
                  "Enable or disable GLVis visualization.");
   args.Parse();
   if (!args.Good())
   {
      args.PrintUsage(cout);
      return 1;
   }
   args.PrintOptions(cout);

   Array<double> coeffs, poles;

   // 2. Compute the coefficients that define the integer-order PDEs.
   ComputePartialFractionApproximation(alpha,coeffs,poles);

   // 3. Read the mesh from the given mesh file.
   Mesh mesh(mesh_file, 1, 1);
   int dim = mesh.Dimension();

   // 4. Refine the mesh to increase the resolution.
   for (int i = 0; i < num_refs; i++)
   {
      mesh.UniformRefinement();
   }

   // 5. Define a finite element space on the mesh.
   FiniteElementCollection *fec = new H1_FECollection(order, dim);

   FiniteElementSpace fespace(&mesh, fec);
   cout << "Number of finite element unknowns: "
        << fespace.GetTrueVSize() << endl;

   // 6. Determine the list of true (i.e. conforming) essential boundary dofs.
   Array<int> ess_tdof_list;
   if (mesh.bdr_attributes.Size())
   {
      Array<int> ess_bdr(mesh.bdr_attributes.Max());
      ess_bdr = 1;
      fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
   }

   // 7. Define diffusion coefficient, load, and solution GridFunction.
   ConstantCoefficient f(1.0);
   ConstantCoefficient one(1.0);
   GridFunction u(&fespace);
   u = 0.;

   // 8. Prepare for visualization.
   char vishost[] = "localhost";
   int  visport   = 19916;
   socketstream xout;
   socketstream uout;
   if (visualization)
   {
      xout.open(vishost, visport);
      xout.precision(8);
      uout.open(vishost, visport);
      uout.precision(8);
   }

   for (int i = 0; i<coeffs.Size(); i++)
   {
      // 9. Set up the linear form b(.) for integer-order PDE solve.
      LinearForm b(&fespace);
      ProductCoefficient cf(coeffs[i], f);
      b.AddDomainIntegrator(new DomainLFIntegrator(cf));
      b.Assemble();

      // 10. Define GridFunction for integer-order PDE solve.
      GridFunction x(&fespace);
      x = 0.0;

      // 11. Set up the bilinear form a(.,.) for integer-order PDE solve.
      BilinearForm a(&fespace);
      a.AddDomainIntegrator(new DiffusionIntegrator(one));
      ConstantCoefficient c2(-poles[i]);
      a.AddDomainIntegrator(new MassIntegrator(c2));
      a.Assemble();

      // 12. Assemble the bilinear form and the corresponding linear system.
      OperatorPtr A;
      Vector B, X;
      a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);

      // 13. Solve the linear system A X = B.
      GSSmoother M((SparseMatrix&)(*A));
      PCG(*A, M, B, X, 1, 200, 1e-12, 0.0);

      // 14. Recover the solution as a finite element grid function.
      a.RecoverFEMSolution(X, b, x);

      // 15. Accumulate integer-order PDE solutions.
      u+=x;

      // 16. Send the solutions by socket to a GLVis server.
      if (visualization)
      {
         xout << "solution\n" << mesh << x << flush;
         uout << "solution\n" << mesh << u << flush;
      }
   }
   delete fec;
   return 0;
}
<commit_msg>plot titles for ex33<commit_after>//                      MFEM Example 33
//
// Compile with: make ex33
//
// Sample runs:  ex33 -m ../data/square-disc.mesh -alpha 0.33 -o 2
//               ex33 -m ../data/star.mesh -alpha 0.99 -o 3
//               ex33 -m ../data/inline-quad.mesh -alpha 0.2 -o 3
//               ex33 -m ../data/disc-nurbs.mesh -alpha 0.33 -o 3
//               ex33 -m ../data/l-shape.mesh -alpha 0.33 -o 3 -r 4
//
//
// Description:
//
//  In this example we solve the following fractional PDE with MFEM:
//
//    ( - Δ )^α u = f  in Ω,      u = 0  on ∂Ω,      0 < α < 1,
//
//  To solve this FPDE, we rely on a rational approximation [2] of the normal
//  linear operator A^{-α}, where A = - Δ (with associated homogenous
//  boundary conditions). Namely, we first approximate the operator
//
//    A^{-α} ≈ Σ_{i=0}^N c_i (A + d_i I)^{-1},      d_0 = 0,   d_i > 0,
//
//  where I is the L2-identity operator and the coefficients c_i and d_i
//  are generated offline to a prescribed accuracy in a pre-processing step.
//  We use the triple-A algorithm [1] to generate the rational approximation
//  that this partial fractional expansion derives from. We then solve N+1
//  independent integer-order PDEs,
//
//    A u_i + d_i u_i = c_i f  in Ω,      u_i = 0  on ∂Ω,      i=0,...,N,
//
//  using MFEM and sum u_i to arrive at an approximate solution of the FPDE
//
//    u ≈ Σ_{i=0}^N u_i.
//
//
// References:
//
// [1] Nakatsukasa, Y., Sète, O., & Trefethen, L. N. (2018). The AAA algorithm
//     for rational approximation. SIAM Journal on Scientific Computing, 40(3),
//     A1494-A1522.
//
// [2] Harizanov, S., Lazarov, R., Margenov, S., Marinov, P., & Pasciak, J.
//     (2020). Analysis of numerical methods for spectral fractional elliptic
//     equations based on the best uniform rational approximation. Journal of
//     Computational Physics, 408, 109285.
//

#include "mfem.hpp"
#include <fstream>
#include <iostream>
#include "ex33.hpp"

using namespace std;
using namespace mfem;

int main(int argc, char *argv[])
{
   // 1. Parse command-line options.
   const char *mesh_file = "../data/star.mesh";
   int order = 1;
   int num_refs = 3;
   bool visualization = true;
   double alpha = 0.5;

   OptionsParser args(argc, argv);
   args.AddOption(&mesh_file, "-m", "--mesh",
                  "Mesh file to use.");
   args.AddOption(&order, "-o", "--order",
                  "Finite element order (polynomial degree) or -1 for"
                  " isoparametric space.");
   args.AddOption(&num_refs, "-r", "--refs",
                  "Number of uniform refinements");
   args.AddOption(&alpha, "-alpha", "--alpha",
                  "Fractional exponent");
   args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
                  "--no-visualization",
                  "Enable or disable GLVis visualization.");
   args.Parse();
   if (!args.Good())
   {
      args.PrintUsage(cout);
      return 1;
   }
   args.PrintOptions(cout);

   Array<double> coeffs, poles;

   // 2. Compute the coefficients that define the integer-order PDEs.
   ComputePartialFractionApproximation(alpha,coeffs,poles);

   // 3. Read the mesh from the given mesh file.
   Mesh mesh(mesh_file, 1, 1);
   int dim = mesh.Dimension();

   // 4. Refine the mesh to increase the resolution.
   for (int i = 0; i < num_refs; i++)
   {
      mesh.UniformRefinement();
   }

   // 5. Define a finite element space on the mesh.
   FiniteElementCollection *fec = new H1_FECollection(order, dim);

   FiniteElementSpace fespace(&mesh, fec);
   cout << "Number of finite element unknowns: "
        << fespace.GetTrueVSize() << endl;

   // 6. Determine the list of true (i.e. conforming) essential boundary dofs.
   Array<int> ess_tdof_list;
   if (mesh.bdr_attributes.Size())
   {
      Array<int> ess_bdr(mesh.bdr_attributes.Max());
      ess_bdr = 1;
      fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
   }

   // 7. Define diffusion coefficient, load, and solution GridFunction.
   ConstantCoefficient f(1.0);
   ConstantCoefficient one(1.0);
   GridFunction u(&fespace);
   u = 0.;

   // 8. Prepare for visualization.
   char vishost[] = "localhost";
   int  visport   = 19916;
   socketstream xout, uout;
   ostringstream oss_x, oss_u;
   if (visualization)
   {
      xout.open(vishost, visport);
      xout.precision(8);
      uout.open(vishost, visport);
      uout.precision(8);
   }

   for (int i = 0; i<coeffs.Size(); i++)
   {
      // 9. Set up the linear form b(.) for integer-order PDE solve.
      LinearForm b(&fespace);
      ProductCoefficient cf(coeffs[i], f);
      b.AddDomainIntegrator(new DomainLFIntegrator(cf));
      b.Assemble();

      // 10. Define GridFunction for integer-order PDE solve.
      GridFunction x(&fespace);
      x = 0.0;

      // 11. Set up the bilinear form a(.,.) for integer-order PDE solve.
      BilinearForm a(&fespace);
      a.AddDomainIntegrator(new DiffusionIntegrator(one));
      ConstantCoefficient c2(-poles[i]);
      a.AddDomainIntegrator(new MassIntegrator(c2));
      a.Assemble();

      // 12. Assemble the bilinear form and the corresponding linear system.
      OperatorPtr A;
      Vector B, X;
      a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);

      // 13. Solve the linear system A X = B.
      GSSmoother M((SparseMatrix&)(*A));
      PCG(*A, M, B, X, 1, 200, 1e-12, 0.0);

      // 14. Recover the solution as a finite element grid function.
      a.RecoverFEMSolution(X, b, x);

      // 15. Accumulate integer-order PDE solutions.
      u+=x;

      // 16. Send the solutions by socket to a GLVis server.
      if (visualization)
      {
         oss_x.str("");
         oss_x.clear();
         oss_x << "Solution of PDE -Δ u + "<<-poles[i]<< " u = " << coeffs[i] << " f" ;
         xout << "solution\n" << mesh << x
              << "window_title '" << oss_x.str() << "'" << flush;

         oss_u.str("");
         oss_u.clear();
         oss_u << "Solution of fractional PDE -Δ^"<<alpha<< " u = f" ;
         uout << "solution\n" << mesh << u
              << "window_title '" << oss_u.str() << "'" << flush;
      }
   }
   delete fec;
   return 0;
}
<|endoftext|>


<commit_before>#include "luacpp/register_any_function.hpp"
#include "luacpp/meta_table.hpp"
#include "luacpp/sink_into_lua.hpp"
#include "luacpp/pcall.hpp"
#include "luacpp/sink_from_lua.hpp"
#include <silicium/asio/tcp_acceptor.hpp>
#include <silicium/asio/writing_observable.hpp>
#include <silicium/observable/end.hpp>
#include <silicium/observable/transform.hpp>
#include <silicium/observable/ptr.hpp>
#include <silicium/observable/generator.hpp>
#include <silicium/observable/constant.hpp>
#include <silicium/source/memory_source.hpp>
#include <silicium/source/generator_source.hpp>
#include <silicium/http/generate_response.hpp>
#include <silicium/sink/iterator_sink.hpp>
#include <boost/program_options.hpp>
#include <iostream>

namespace
{
	struct tcp_client : private Si::observer<boost::system::error_code>
	{
		explicit tcp_client(lua::main_thread main_thread, std::shared_ptr<boost::asio::ip::tcp::socket> socket)
			: m_main_thread(main_thread)
			, m_socket(std::move(socket))
			, m_sender(Si::asio::make_writing_observable(*m_socket, Si::erase_unique(Si::make_generator_observable([this]() -> Si::memory_range
			{
				return Si::make_memory_range(m_send_buffer);
			}))))
		{
			assert(m_socket);
		}

		void append(lua_Integer element)
		{
			m_send_buffer.push_back(static_cast<char>(element));
		}

		void flush(lua::current_thread thread)
		{
			boost::optional<lua::coroutine> coro = lua::pin_coroutine(m_main_thread, thread);
			if (!coro)
			{
				std::terminate(); //TODO
				return;
			}
			m_sender.async_get_one(*this);
			m_coro = std::move(*coro);
			m_coro.suspend();
		}

	private:

		lua::main_thread m_main_thread;
		std::shared_ptr<boost::asio::ip::tcp::socket> m_socket;
		Si::noexcept_string m_send_buffer;
		Si::asio::writing_observable<boost::asio::ip::tcp::socket, Si::unique_observable<Si::memory_range>> m_sender;
		lua::coroutine m_coro;

		virtual void got_element(boost::system::error_code value) SILICIUM_OVERRIDE
		{
			boost::ignore_unused_variable_warning(value);
			auto coro = std::move(m_coro);
			coro.resume(0);

			boost::system::error_code ignored;
			m_socket->shutdown(boost::asio::ip::tcp::socket::shutdown_send, ignored);

			for (;;)
			{
				char dummy_buffer;
				m_socket->read_some(boost::asio::buffer(&dummy_buffer, 1), ignored);
				if (ignored)
				{
					break;
				}
			}
		}

		virtual void ended() SILICIUM_OVERRIDE
		{
			SILICIUM_UNREACHABLE();
		}
	};

	struct tcp_acceptor : private Si::observer<Si::asio::tcp_acceptor_result>
	{
		explicit tcp_acceptor(lua::main_thread main_thread, boost::asio::io_service &io, boost::asio::ip::tcp::endpoint endpoint)
			: m_main_thread(main_thread)
			, m_acceptor(io, endpoint)
			, m_incoming_clients(m_acceptor)
		{
		}

		void sync_get(lua::current_thread thread)
		{
			boost::optional<lua::coroutine> coro = lua::pin_coroutine(m_main_thread, thread);
			if (!coro)
			{
				std::terminate(); //TODO
				return;
			}
			m_waiting = true;
			m_incoming_clients.async_get_one(static_cast<Si::observer<Si::asio::tcp_acceptor_result> &>(*this));
			if (m_waiting)
			{
				m_suspended = std::move(*coro);
				return m_suspended->suspend();
			}
		}

	private:

		lua::main_thread m_main_thread;
		boost::asio::ip::tcp::acceptor m_acceptor;
		Si::asio::tcp_acceptor m_incoming_clients;
		bool m_waiting;
		boost::optional<lua::coroutine> m_suspended;

		virtual void got_element(Si::asio::tcp_acceptor_result incoming) SILICIUM_OVERRIDE
		{
			assert(m_waiting);
			assert(m_suspended);
			m_waiting = false;

			assert(lua_status(&m_suspended->thread()) == LUA_YIELD);

			if (incoming.is_error())
			{
				return Si::exchange(m_suspended, boost::none)->resume(1);
			}

			lua::coroutine child_coro = *std::move(Si::exchange(m_suspended, boost::none));
			lua::stack child_stack(child_coro.thread());

			assert(child_stack.size() == 0);

			lua::stack_value meta = lua::create_default_meta_table<tcp_client>(child_stack);
			assert(child_stack.size() == 1);
			assert(child_stack.get_type(meta) == lua::type::table);

			add_method(child_stack, meta, "append", &tcp_client::append);
			assert(child_stack.size() == 1);
			assert(child_stack.get_type(meta) == lua::type::table);

			lua::add_method(child_stack, meta, "flush", &tcp_client::flush);
			assert(child_stack.size() == 1);
			assert(child_stack.get_type(meta) == lua::type::table);

			lua::stack_value client = lua::emplace_object<tcp_client>(child_stack, meta, m_main_thread, incoming.get());
			replace(client, meta);

			assert(lua_gettop(child_stack.state()) == 1);
			client.release();
			child_coro.resume(1);
			assert(lua_gettop(child_stack.state()) == 0);
		}

		virtual void ended() SILICIUM_OVERRIDE
		{
			SILICIUM_UNREACHABLE();
		}
	};

	struct http_response_generator
	{
		explicit http_response_generator(lua::reference sink)
			: m_sink(std::move(sink))
		{
			assert(m_sink.get_type() == lua::type::user_data);
		}

		void status_line(Si::memory_range status, Si::memory_range status_text, Si::memory_range version, lua_State &state)
		{
			lua::text_sink_into_lua native_sink(m_sink, state);
			Si::http::generate_status_line(native_sink, version, status, status_text);
		}

		void header(Si::memory_range key, Si::memory_range value, lua_State &state)
		{
			lua::text_sink_into_lua native_sink(m_sink, state);
			Si::http::generate_header(native_sink, key, value);
		}

		void content(Si::memory_range content, lua_State &state)
		{
			lua::text_sink_into_lua native_sink(m_sink, state);
			Si::append(native_sink, "\r\n");
			native_sink.append(content);
		}

	private:

		lua::reference m_sink;
	};

	lua::stack_value require_package(
		lua::main_thread main_thread,
		lua::stack &stack,
		boost::asio::io_service &io,
		Si::noexcept_string const &name,
		Si::noexcept_string const &version)
	{
		if (name == "tcp")
		{
			lua::stack_value module = stack.create_table();
			stack.set_element(
				module,
				"create_acceptor",
				[main_thread, &stack, &io](lua_State &)
			{
				return lua::register_any_function(stack, [main_thread, &stack, &io](lua_Integer port)
				{
					boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address_v4::any(), static_cast<boost::uint16_t>(port));
					return lua::emplace_object< ::tcp_acceptor>(stack, [&stack](lua_State &)
					{
						lua::stack_value meta = lua::create_default_meta_table< ::tcp_acceptor>(stack);
						add_method(stack, meta, "sync_get", &tcp_acceptor::sync_get);
						return meta;
					}, main_thread, io, endpoint);
				});
			});
			module.assert_top();
			return module;
		}
		else if (name == "http")
		{
			lua::stack_value module = stack.create_table();
			stack.set_element(
				module,
				"make_response_generator",
				[main_thread, &stack, &io](lua_State &)
			{
				return lua::register_any_function(stack, [main_thread, &stack, &io](lua::any_local const &sink)
				{
					lua::stack_value meta = lua::create_default_meta_table<http_response_generator>(stack);
					lua::add_method(stack, meta, "status_line", &http_response_generator::status_line);
					lua::add_method(stack, meta, "header", &http_response_generator::header);
					lua::add_method(stack, meta, "content", &http_response_generator::content);

					lua::reference sink_kept_alive = lua::create_reference(main_thread, sink);
					assert(sink_kept_alive.get_type() == lua::type::user_data);

					lua::stack_value generator = lua::emplace_object<http_response_generator>(stack, meta, std::move(sink_kept_alive));
					lua::replace(generator, meta);
					return generator;
				});
			});
			module.assert_top();
			return module;
		}
		return stack.push_nil();
	}

	struct options
	{
		std::string program;
	};

	boost::optional<options> parse_options(int argc, char **argv)
	{
		options parsed;

		boost::program_options::options_description desc("Allowed options");
		desc.add_options()
		    ("help", "produce help message")
			("program", boost::program_options::value(&parsed.program), "the Lua code file to execute")
		;

		boost::program_options::positional_options_description positional;
		positional.add("program", 1);
		boost::program_options::variables_map vm;
		try
		{
			boost::program_options::store(boost::program_options::command_line_parser(argc, argv).options(desc).positional(positional).run(), vm);
		}
		catch (boost::program_options::error const &ex)
		{
			std::cerr
				<< ex.what() << '\n'
				<< desc << "\n";
			return boost::none;
		}

		boost::program_options::notify(vm);

		if (vm.count("help"))
		{
		    std::cerr << desc << "\n";
		    return boost::none;
		}

		return parsed;
	}
}

int main(int argc, char **argv)
{
	boost::optional<options> parsed_options = parse_options(argc, argv);
	if (!parsed_options)
	{
		return 1;
	}

	boost::asio::io_service io;

	auto state = lua::create_lua();
	lua_atpanic(state.get(), [](lua_State *L) -> int
	{
		char const *message = lua_tostring(L, -1);
		std::cerr << message << '\n';
		std::terminate();
	});
	luaopen_base(state.get());
	luaopen_string(state.get());
	lua::stack stack(*state);
	std::pair<lua::error, lua::stack_value> first_level = stack.load_file(parsed_options->program);
	if (first_level.first != lua::error::success)
	{
		std::cerr << stack.to_string(first_level.second) << '\n';
		return 1;
	}

	try
	{
		lua::stack_value second_level = stack.call(first_level.second, lua::no_arguments(), std::integral_constant<int, 1>());
		lua::main_thread main_thread(*state);
		lua::coroutine runner = lua::create_coroutine(main_thread);
		lua::stack runner_stack(runner.thread());
		lua::stack::resume_result resumed = runner_stack.resume(
			lua::xmove(std::move(second_level), runner.thread()),
			Si::make_oneshot_generator_source([main_thread, &runner_stack, &io]()
		{
			return lua::register_any_function(runner_stack, [main_thread, &runner_stack, &io](Si::noexcept_string const &name, Si::noexcept_string const &version)
			{
				return require_package(main_thread, runner_stack, io, name, version);
			});
		}));
		assert(Si::try_get_ptr<lua::stack::yield>(resumed));
		io.run();
	}
	catch (std::exception const &ex)
	{
		std::cerr << ex.what() << '\n';
		return 1;
	}
}
<commit_msg>cache the tcp client meta table<commit_after>#include "luacpp/register_any_function.hpp"
#include "luacpp/meta_table.hpp"
#include "luacpp/sink_into_lua.hpp"
#include "luacpp/pcall.hpp"
#include "luacpp/sink_from_lua.hpp"
#include <silicium/asio/tcp_acceptor.hpp>
#include <silicium/asio/writing_observable.hpp>
#include <silicium/observable/end.hpp>
#include <silicium/observable/transform.hpp>
#include <silicium/observable/ptr.hpp>
#include <silicium/observable/generator.hpp>
#include <silicium/observable/constant.hpp>
#include <silicium/source/memory_source.hpp>
#include <silicium/source/generator_source.hpp>
#include <silicium/http/generate_response.hpp>
#include <silicium/sink/iterator_sink.hpp>
#include <boost/program_options.hpp>
#include <iostream>

namespace
{
	struct tcp_client : private Si::observer<boost::system::error_code>
	{
		explicit tcp_client(lua::main_thread main_thread, std::shared_ptr<boost::asio::ip::tcp::socket> socket)
			: m_main_thread(main_thread)
			, m_socket(std::move(socket))
			, m_sender(Si::asio::make_writing_observable(*m_socket, Si::erase_unique(Si::make_generator_observable([this]() -> Si::memory_range
			{
				return Si::make_memory_range(m_send_buffer);
			}))))
		{
			assert(m_socket);
		}

		void append(lua_Integer element)
		{
			m_send_buffer.push_back(static_cast<char>(element));
		}

		void flush(lua::current_thread thread)
		{
			boost::optional<lua::coroutine> coro = lua::pin_coroutine(m_main_thread, thread);
			if (!coro)
			{
				std::terminate(); //TODO
				return;
			}
			m_sender.async_get_one(*this);
			m_coro = std::move(*coro);
			m_coro.suspend();
		}

	private:

		lua::main_thread m_main_thread;
		std::shared_ptr<boost::asio::ip::tcp::socket> m_socket;
		Si::noexcept_string m_send_buffer;
		Si::asio::writing_observable<boost::asio::ip::tcp::socket, Si::unique_observable<Si::memory_range>> m_sender;
		lua::coroutine m_coro;

		virtual void got_element(boost::system::error_code value) SILICIUM_OVERRIDE
		{
			boost::ignore_unused_variable_warning(value);
			auto coro = std::move(m_coro);
			coro.resume(0);

			boost::system::error_code ignored;
			m_socket->shutdown(boost::asio::ip::tcp::socket::shutdown_send, ignored);

			for (;;)
			{
				char dummy_buffer;
				m_socket->read_some(boost::asio::buffer(&dummy_buffer, 1), ignored);
				if (ignored)
				{
					break;
				}
			}
		}

		virtual void ended() SILICIUM_OVERRIDE
		{
			SILICIUM_UNREACHABLE();
		}
	};

	inline lua::stack_value create_tcp_client_meta_table(lua::stack &s)
	{
		lua::stack_value meta = lua::create_default_meta_table<tcp_client>(s);
		assert(s.size() == 1);
		assert(s.get_type(meta) == lua::type::table);

		add_method(s, meta, "append", &tcp_client::append);
		assert(s.size() == 1);
		assert(s.get_type(meta) == lua::type::table);

		lua::add_method(s, meta, "flush", &tcp_client::flush);
		assert(s.size() == 1);
		assert(s.get_type(meta) == lua::type::table);
		return meta;
	}

	struct tcp_acceptor : private Si::observer<Si::asio::tcp_acceptor_result>
	{
		explicit tcp_acceptor(
			lua::main_thread main_thread,
			boost::asio::io_service &io,
			boost::asio::ip::tcp::endpoint endpoint)
			: m_main_thread(main_thread)
			, m_acceptor(io, endpoint)
			, m_incoming_clients(m_acceptor)
		{
		}

		void sync_get(lua::current_thread thread)
		{
			boost::optional<lua::coroutine> coro = lua::pin_coroutine(m_main_thread, thread);
			if (!coro)
			{
				std::terminate(); //TODO
				return;
			}
			m_waiting = true;
			m_incoming_clients.async_get_one(static_cast<Si::observer<Si::asio::tcp_acceptor_result> &>(*this));
			if (m_waiting)
			{
				m_suspended = std::move(*coro);
				return m_suspended->suspend();
			}
		}

	private:

		lua::main_thread m_main_thread;
		boost::asio::ip::tcp::acceptor m_acceptor;
		Si::asio::tcp_acceptor m_incoming_clients;
		bool m_waiting;
		boost::optional<lua::coroutine> m_suspended;
		lua::reference m_cached_client_meta_table;

		virtual void got_element(Si::asio::tcp_acceptor_result incoming) SILICIUM_OVERRIDE
		{
			assert(m_waiting);
			assert(m_suspended);
			m_waiting = false;

			assert(lua_status(&m_suspended->thread()) == LUA_YIELD);

			if (incoming.is_error())
			{
				return Si::exchange(m_suspended, boost::none)->resume(1);
			}

			lua::coroutine child_coro = *std::move(Si::exchange(m_suspended, boost::none));
			lua::stack child_stack(child_coro.thread());
			assert(child_stack.size() == 0);

			if (m_cached_client_meta_table.empty())
			{
				m_cached_client_meta_table = lua::create_reference(m_main_thread, create_tcp_client_meta_table(child_stack));
			}
			lua::stack_value client = lua::emplace_object<tcp_client>(child_stack, m_cached_client_meta_table, m_main_thread, incoming.get());

			assert(lua_gettop(child_stack.state()) == 1);
			client.release();
			child_coro.resume(1);
			assert(lua_gettop(child_stack.state()) == 0);
		}

		virtual void ended() SILICIUM_OVERRIDE
		{
			SILICIUM_UNREACHABLE();
		}
	};

	struct http_response_generator
	{
		explicit http_response_generator(lua::reference sink)
			: m_sink(std::move(sink))
		{
			assert(m_sink.get_type() == lua::type::user_data);
		}

		void status_line(Si::memory_range status, Si::memory_range status_text, Si::memory_range version, lua_State &state)
		{
			lua::text_sink_into_lua native_sink(m_sink, state);
			Si::http::generate_status_line(native_sink, version, status, status_text);
		}

		void header(Si::memory_range key, Si::memory_range value, lua_State &state)
		{
			lua::text_sink_into_lua native_sink(m_sink, state);
			Si::http::generate_header(native_sink, key, value);
		}

		void content(Si::memory_range content, lua_State &state)
		{
			lua::text_sink_into_lua native_sink(m_sink, state);
			Si::append(native_sink, "\r\n");
			native_sink.append(content);
		}

	private:

		lua::reference m_sink;
	};

	lua::stack_value require_package(
		lua::main_thread main_thread,
		lua::stack &stack,
		boost::asio::io_service &io,
		Si::noexcept_string const &name,
		Si::noexcept_string const &version)
	{
		if (name == "tcp")
		{
			lua::stack_value module = stack.create_table();
			stack.set_element(
				module,
				"create_acceptor",
				[main_thread, &stack, &io](lua_State &)
			{
				return lua::register_any_function(stack, [main_thread, &stack, &io](lua_Integer port)
				{
					boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address_v4::any(), static_cast<boost::uint16_t>(port));
					return lua::emplace_object< ::tcp_acceptor>(stack, [&stack](lua_State &)
					{
						lua::stack_value meta = lua::create_default_meta_table< ::tcp_acceptor>(stack);
						add_method(stack, meta, "sync_get", &tcp_acceptor::sync_get);
						return meta;
					}, main_thread, io, endpoint);
				});
			});
			module.assert_top();
			return module;
		}
		else if (name == "http")
		{
			lua::stack_value module = stack.create_table();
			stack.set_element(
				module,
				"make_response_generator",
				[main_thread, &stack, &io](lua_State &)
			{
				return lua::register_any_function(stack, [main_thread, &stack, &io](lua::any_local const &sink)
				{
					lua::stack_value meta = lua::create_default_meta_table<http_response_generator>(stack);
					lua::add_method(stack, meta, "status_line", &http_response_generator::status_line);
					lua::add_method(stack, meta, "header", &http_response_generator::header);
					lua::add_method(stack, meta, "content", &http_response_generator::content);

					lua::reference sink_kept_alive = lua::create_reference(main_thread, sink);
					assert(sink_kept_alive.get_type() == lua::type::user_data);

					lua::stack_value generator = lua::emplace_object<http_response_generator>(stack, meta, std::move(sink_kept_alive));
					lua::replace(generator, meta);
					return generator;
				});
			});
			module.assert_top();
			return module;
		}
		return stack.push_nil();
	}

	struct options
	{
		std::string program;
	};

	boost::optional<options> parse_options(int argc, char **argv)
	{
		options parsed;

		boost::program_options::options_description desc("Allowed options");
		desc.add_options()
		    ("help", "produce help message")
			("program", boost::program_options::value(&parsed.program), "the Lua code file to execute")
		;

		boost::program_options::positional_options_description positional;
		positional.add("program", 1);
		boost::program_options::variables_map vm;
		try
		{
			boost::program_options::store(boost::program_options::command_line_parser(argc, argv).options(desc).positional(positional).run(), vm);
		}
		catch (boost::program_options::error const &ex)
		{
			std::cerr
				<< ex.what() << '\n'
				<< desc << "\n";
			return boost::none;
		}

		boost::program_options::notify(vm);

		if (vm.count("help"))
		{
		    std::cerr << desc << "\n";
		    return boost::none;
		}

		return parsed;
	}
}

int main(int argc, char **argv)
{
	boost::optional<options> parsed_options = parse_options(argc, argv);
	if (!parsed_options)
	{
		return 1;
	}

	boost::asio::io_service io;

	auto state = lua::create_lua();
	lua_atpanic(state.get(), [](lua_State *L) -> int
	{
		char const *message = lua_tostring(L, -1);
		std::cerr << message << '\n';
		std::terminate();
	});
	luaopen_base(state.get());
	luaopen_string(state.get());
	lua::stack stack(*state);
	std::pair<lua::error, lua::stack_value> first_level = stack.load_file(parsed_options->program);
	if (first_level.first != lua::error::success)
	{
		std::cerr << stack.to_string(first_level.second) << '\n';
		return 1;
	}

	try
	{
		lua::stack_value second_level = stack.call(first_level.second, lua::no_arguments(), std::integral_constant<int, 1>());
		lua::main_thread main_thread(*state);
		lua::coroutine runner = lua::create_coroutine(main_thread);
		lua::stack runner_stack(runner.thread());
		lua::stack::resume_result resumed = runner_stack.resume(
			lua::xmove(std::move(second_level), runner.thread()),
			Si::make_oneshot_generator_source([main_thread, &runner_stack, &io]()
		{
			return lua::register_any_function(runner_stack, [main_thread, &runner_stack, &io](Si::noexcept_string const &name, Si::noexcept_string const &version)
			{
				return require_package(main_thread, runner_stack, io, name, version);
			});
		}));
		assert(Si::try_get_ptr<lua::stack::yield>(resumed));
		io.run();
	}
	catch (std::exception const &ex)
	{
		std::cerr << ex.what() << '\n';
		return 1;
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 Google, Inc.
//
//  simple JPEG compressor or re-compressor
//
// usage:
//   sjpeg input.{jpg,png} [-o output.jpg] [-q quality]
//
// 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.
//
// Author: Skal (pascal.massimino@gmail.com)
//

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <vector>
using std::vector;

#include "sjpeg.h"
#include "./utils.h"

///////////////////////////////////////////////////////////////////////////////

void PrintMatrix(const char name[], const uint8_t m[64], bool for_chroma) {
  fprintf(stdout, " %s quantization matrix (estimated quality: %.1f)\n",
          name, SjpegEstimateQuality(m, for_chroma));
  for (int j = 0; j < 8; ++j) {
    for (int i = 0; i < 8; ++i) fprintf(stdout, "%3d ", m[i + j * 8]);
    fprintf(stdout, "\n");
  }
  fprintf(stdout, "------\n");
}

///////////////////////////////////////////////////////////////////////////////

static const char* kYUVModeNames[4] = {
  "automatic", "YUV420", "SharpYUV420", "YUV444"
};
static const char* kNoYes[2] = { "no", "yes" };

int main(int argc, char * argv[]) {
  const char* input_file = NULL;
  const char* output_file = NULL;
  SjpegEncodeParam param;
  float reduction = 100;
  float quality = 75;
  bool use_reduction = true;  // until '-q' is used...
  bool no_metadata = false;
  bool estimate = false;
  bool limit_quantization = true;
  int info = 0;
  bool quiet = false;
  bool short_output = false;
  float riskiness = 0;
  SjpegYUVMode yuv_mode_rec = SJPEG_YUV_AUTO;
  const char* const usage =
    "sjpeg: Commandline utility to recompress or compress pictures to JPEG.\n"
    "Usage:  sjpeg infile [-o outfile.jpg] [-q quality] ...\n"
    "  -q <float> ...... Quality factor in [0..100] range.\n"
    "                    Value of 100 gives the best quality, largest file.\n"
    "                    Default value is 75.\n"
    "  -r <float> ...... Reduction factor in [0..100] range.\n"
    "                    Default value is 100. Lower value will reduce the \n"
    "                    file size.\n"
    "  -o filename ..... specifies the output file name.\n"
    "  -size <int> ..... target size\n"
    "  -psnr <float> ... target YUV-PSNR\n"
    "  -estimate ....... Just estimate and print the JPEG source quality.\n"
    "  -i .............. Just print some information about the input file.\n"
    "  -version ........ Print the version and exit.\n"
    "  -quiet .......... Quiet mode. Just save the file.\n"
    "  -short .......... Print shorter 1-line info.\n"
    "\n"
    "Advanced options:\n"
    "  -yuv_mode .......... YUV mode to use:\n"
    "                       0: automatic decision (default)\n"
    "                       1: use YUV 4:2:0\n"
    "                       2: use 'Sharp' YUV 4:2:0 conversion\n"
    "                       3: use YUV 4:4:4 (full resolution for U/V planes)\n"
    "  -no_limit .......... If true, allow the quality factor to be larger\n"
    "                       than the original (JPEG input only).\n"
    "  -no_optim .......... Don't use Huffman optimization (=faster)\n"
    "  -no_adapt .......... Don't use adaptive quantization (=faster)\n"
    "  -trellis ........... use trellis-based quantization (=slower)\n"
    "  -no_metadata ....... Ignore metadata from the source.\n"
    "  -pass <int> ........ number of passes for -size or -psnr (default: 10\n"
    "  -qmin <float> ...... minimum acceptable quality factor during search\n"
    "  -qmax <float> ...... maximum acceptable quality factor during search\n"
    "  -tolerance <float> . tolerance for convergence during search\n"
    "\n"
    "  -444 ............... shortcut for '-yuv_mode 3'\n"
    "  -sharp ............. shortcut for '-yuv_mode 2'\n"
    "  -420 ............... shortcut for '-yuv_mode 1'\n"
    "\n"
    "If the input format is JPEG, the recompression will not go beyond the\n"
    "original quality, *except* if '-no_limit' option is used."
    "\n";

#if defined(ALT_HOOK_CLASS)
  ALT_HOOK_CLASS hook;
  param.search_hook = &hook;
#endif

  // parse command line
  if (argc <= 1) {
    fprintf(stderr, usage);
    return 0;
  }
  for (int c = 1; c < argc; ++c) {
    if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "--help")) {
      fprintf(stdout, usage);
      return 0;
    } else if (!strcmp(argv[c], "-o") && c + 1 < argc) {
      output_file = argv[++c];
    } else if (!strcmp(argv[c], "-q") && c + 1 < argc) {
      quality = atof(argv[++c]);
      use_reduction = false;
      if (quality < 0 || quality > 100) {
        fprintf(stdout, "Error: invalid range for option '%s': %s\n",
                argv[c - 1], argv[c]);
        return 1;
      }
    } else if (!strcmp(argv[c], "-r") && c + 1 < argc) {
      reduction = atof(argv[++c]);
      use_reduction = true;
      if (reduction <= 0 || reduction > 100) {
        fprintf(stdout, "Error: invalid range for option '%s': %s\n",
                argv[c - 1], argv[c]);
        return 1;
      }
    } else if (!strcmp(argv[c], "-estimate")) {
      estimate = true;
    } else if (!strcmp(argv[c], "-no_limit")) {
      limit_quantization = false;
    } else if (!strcmp(argv[c], "-no_adapt")) {
      param.adaptive_quantization = false;
    } else if (!strcmp(argv[c], "-no_optim")) {
      param.Huffman_compress = false;
    } else if (!strcmp(argv[c], "-adapt_bias")) {
      param.adaptive_bias = true;
    } else if (!strcmp(argv[c], "-trellis")) {
      param.use_trellis = true;
    } else if (!strcmp(argv[c], "-psnr") && c + 1 < argc) {
      param.target_mode = SjpegEncodeParam::TARGET_PSNR;
      param.target_value = atof(argv[++c]);
    } else if (!strcmp(argv[c], "-tolerance")) {
      param.tolerance = atof(argv[++c]);
    } else if (!strcmp(argv[c], "-qmin")) {
      param.qmin = atof(argv[++c]);
    } else if (!strcmp(argv[c], "-qmax")) {
      param.qmax = atof(argv[++c]);
    } else if (!strcmp(argv[c], "-size") && c + 1 < argc) {
      param.target_mode = SjpegEncodeParam::TARGET_SIZE;
      param.target_value = atof(argv[++c]);
    } else if (!strcmp(argv[c], "-pass") && c + 1 < argc) {
      param.passes = atoi(argv[++c]);
    } else if (!strcmp(argv[c], "-no_metadata")) {
      no_metadata = true;
    } else if (!strcmp(argv[c], "-yuv_mode") && c + 1 < argc) {
      const int mode = atoi(argv[++c]);
      if (mode < 0 || mode > 3) {
        fprintf(stdout, "Error: invalid range for option '%s': %s\n",
                argv[c - 1], argv[c]);
        return 1;
      }
      param.yuv_mode = (SjpegYUVMode)mode;
    } else if (!strcmp(argv[c], "-i") || !strcmp(argv[c], "-info")) {
      info = true;
    } else if (!strcmp(argv[c], "-quiet")) {
      quiet = true;
    } else if (!strcmp(argv[c], "-short")) {
      short_output = true;
    } else if (!strcmp(argv[c], "-version")) {
      const uint32_t version = SjpegVersion();
      fprintf(stdout, "%d.%d.%d\n",
              (version >> 16) & 0xff,
              (version >>  8) & 0xff,
              (version >>  0) & 0xff);
      return 0;
    } else {
      input_file = argv[c];
    }
  }
  if (input_file == NULL) {
    fprintf(stderr, "Missing input file.\n");
    if (!quiet) fprintf(stderr, usage);
    return -1;
  }
  // finish param set up
  if (param.target_mode != SjpegEncodeParam::TARGET_NONE && param.passes <= 1) {
    param.passes = 10;
  }
  // Read input file into the buffer in_bytes[]
  std::string input = ReadFile(input_file);
  if (input.size() == 0) return 1;

  const ImageType input_type = GuessImageType(input);
  uint8_t quant_matrices[2][64];
  const int nb_matrices =
    (input_type == SJPEG_JPEG) ? SjpegFindQuantizer(input, quant_matrices)
                               : 0;
  const bool is_jpeg = (input_type == SJPEG_JPEG) && (nb_matrices > 0);
  if (use_reduction && !is_jpeg) {
    if (!quiet && !short_output) {
      fprintf(stdout, "Warning! reduction factor (-r option) disabled"
                      " (only applies to JPEG source).\n");
      fprintf(stdout, "         Please use the -q option to set the"
                      " quality factor.\n\n");
    }
    use_reduction = false;
  }
  if (use_reduction) {   // use 'reduction' factor for JPEG source
    param.SetQuantMatrix(quant_matrices[0], 0, reduction);
    param.SetQuantMatrix(quant_matrices[1], 1, reduction);
    param.SetLimitQuantization(true);
  } else {    // the '-q' option has been used.
    param.SetQuality(quality);
    param.SetLimitQuantization(false);
  }

  if (estimate) {
    const int q = is_jpeg ? SjpegEstimateQuality(quant_matrices[0], 0) : 100;
    fprintf(stdout, "%d\n", q);
    return 0;
  }
  int W, H;
  vector<uint8_t> in_bytes = ReadImage(input, &W, &H, &param);
  if (in_bytes.size() == 0) return 1;

  if (!short_output && !quiet) {
    fprintf(stdout, "Input [%s]: %s (%ld bytes, %d x %d)\n",
            ImageTypeName(input_type), input_file,
            static_cast<long>(input.size()),
            W, H);
    if (info) {
      yuv_mode_rec = SjpegRiskiness(&in_bytes[0], W, H, 3 * W, &riskiness);
      fprintf(stdout, "Riskiness:   %.1f (recommended yuv_mode: %s)\n",
              riskiness, kYUVModeNames[yuv_mode_rec]);

      if (is_jpeg) {
        fprintf(stdout, "Input is JPEG w/ %d matrices:\n", nb_matrices);
        if (nb_matrices > 0) {
          PrintMatrix("Luma", quant_matrices[0], false);
        }
        if (nb_matrices > 1) {
          PrintMatrix("UV-chroma", quant_matrices[1], true);
        }
      }
    }
  }
  if (info) return 0;   // done

  // finish setting up the quantization matrices
  if (limit_quantization == false) param.SetLimitQuantization(false);

  if (no_metadata) param.ResetMetadata();

  const double start = GetStopwatchTime();
  const std::string out = SjpegEncode(&in_bytes[0], W, H, 3 * W, param);
  const double encode_time = GetStopwatchTime() - start;

  if (out.size() == 0) {
    fprintf(stderr, "ERROR: call to SjpegEncode() failed.\n");
    return -1;
  }

  if (param.target_mode != SjpegEncodeParam::TARGET_NONE &&
      param.search_hook != nullptr) {
    quality = param.search_hook->q;  // retrieve the final quality used
  }

  if (!short_output && !quiet) {
    yuv_mode_rec = SjpegRiskiness(&in_bytes[0], W, H, 3 * W, &riskiness);
    fprintf(stdout, "new size:   %ld bytes (%.2lf%% of original)\n"
                    "%s%.1f (adaptive: %s, Huffman: %s)\n"
                    "yuv mode:   %s (riskiness: %.1lf%%)\n"
                    "elapsed:    %d ms\n",
                    (long)out.size(), 100. * out.size() / input.size(),
                    use_reduction ? "reduction:  r=" : "quality:    q=",
                    use_reduction ? reduction : quality,
                    kNoYes[param.adaptive_quantization],
                    kNoYes[param.Huffman_compress],
                    kYUVModeNames[yuv_mode_rec], riskiness,
                    static_cast<int>(1000. * encode_time));
    if (param.iccp.size()) {
      fprintf(stdout, "ICCP:       %ld bytes\n", (long)param.iccp.size());
    }
    if (param.exif.size()) {
      fprintf(stdout, "EXIF:       %ld bytes\n", (long)param.exif.size());
    }
    if (param.xmp.size()) {
      fprintf(stdout, "XMP:        %ld bytes\n", (long)param.xmp.size());
    }
  } else if (!quiet) {
    fprintf(stdout, "%ld %ld %.2lf %%\n",
           static_cast<long>(input.size()), static_cast<long>(out.size()),
           100. * out.size() / input.size());
  }

  // Save the result.
  if (!SaveFile(output_file, out, quiet)) return 1;

  return 0;     // ok.
}

///////////////////////////////////////////////////////////////////////////////
<commit_msg>add dropped -sharp_yuv, -444 and -420 parsing<commit_after>// Copyright 2017 Google, Inc.
//
//  simple JPEG compressor or re-compressor
//
// usage:
//   sjpeg input.{jpg,png} [-o output.jpg] [-q quality]
//
// 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.
//
// Author: Skal (pascal.massimino@gmail.com)
//

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <vector>
using std::vector;

#include "sjpeg.h"
#include "./utils.h"

///////////////////////////////////////////////////////////////////////////////

void PrintMatrix(const char name[], const uint8_t m[64], bool for_chroma) {
  fprintf(stdout, " %s quantization matrix (estimated quality: %.1f)\n",
          name, SjpegEstimateQuality(m, for_chroma));
  for (int j = 0; j < 8; ++j) {
    for (int i = 0; i < 8; ++i) fprintf(stdout, "%3d ", m[i + j * 8]);
    fprintf(stdout, "\n");
  }
  fprintf(stdout, "------\n");
}

///////////////////////////////////////////////////////////////////////////////

static const char* kYUVModeNames[4] = {
  "automatic", "YUV420", "SharpYUV420", "YUV444"
};
static const char* kNoYes[2] = { "no", "yes" };

int main(int argc, char * argv[]) {
  const char* input_file = NULL;
  const char* output_file = NULL;
  SjpegEncodeParam param;
  float reduction = 100;
  float quality = 75;
  bool use_reduction = true;  // until '-q' is used...
  bool no_metadata = false;
  bool estimate = false;
  bool limit_quantization = true;
  int info = 0;
  bool quiet = false;
  bool short_output = false;
  float riskiness = 0;
  SjpegYUVMode yuv_mode_rec = SJPEG_YUV_AUTO;
  const char* const usage =
    "sjpeg: Commandline utility to recompress or compress pictures to JPEG.\n"
    "Usage:  sjpeg infile [-o outfile.jpg] [-q quality] ...\n"
    "  -q <float> ...... Quality factor in [0..100] range.\n"
    "                    Value of 100 gives the best quality, largest file.\n"
    "                    Default value is 75.\n"
    "  -r <float> ...... Reduction factor in [0..100] range.\n"
    "                    Default value is 100. Lower value will reduce the \n"
    "                    file size.\n"
    "  -o filename ..... specifies the output file name.\n"
    "  -size <int> ..... target size\n"
    "  -psnr <float> ... target YUV-PSNR\n"
    "  -estimate ....... Just estimate and print the JPEG source quality.\n"
    "  -i .............. Just print some information about the input file.\n"
    "  -version ........ Print the version and exit.\n"
    "  -quiet .......... Quiet mode. Just save the file.\n"
    "  -short .......... Print shorter 1-line info.\n"
    "\n"
    "Advanced options:\n"
    "  -yuv_mode .......... YUV mode to use:\n"
    "                       0: automatic decision (default)\n"
    "                       1: use YUV 4:2:0\n"
    "                       2: use 'Sharp' YUV 4:2:0 conversion\n"
    "                       3: use YUV 4:4:4 (full resolution for U/V planes)\n"
    "  -no_limit .......... If true, allow the quality factor to be larger\n"
    "                       than the original (JPEG input only).\n"
    "  -no_optim .......... Don't use Huffman optimization (=faster)\n"
    "  -no_adapt .......... Don't use adaptive quantization (=faster)\n"
    "  -trellis ........... use trellis-based quantization (=slower)\n"
    "  -no_metadata ....... Ignore metadata from the source.\n"
    "  -pass <int> ........ number of passes for -size or -psnr (default: 10\n"
    "  -qmin <float> ...... minimum acceptable quality factor during search\n"
    "  -qmax <float> ...... maximum acceptable quality factor during search\n"
    "  -tolerance <float> . tolerance for convergence during search\n"
    "\n"
    "  -444 ............... shortcut for '-yuv_mode 3'\n"
    "  -sharp ............. shortcut for '-yuv_mode 2'\n"
    "  -420 ............... shortcut for '-yuv_mode 1'\n"
    "\n"
    "If the input format is JPEG, the recompression will not go beyond the\n"
    "original quality, *except* if '-no_limit' option is used."
    "\n";

#if defined(ALT_HOOK_CLASS)
  ALT_HOOK_CLASS hook;
  param.search_hook = &hook;
#endif

  // parse command line
  if (argc <= 1) {
    fprintf(stderr, usage);
    return 0;
  }
  for (int c = 1; c < argc; ++c) {
    if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "--help")) {
      fprintf(stdout, usage);
      return 0;
    } else if (!strcmp(argv[c], "-o") && c + 1 < argc) {
      output_file = argv[++c];
    } else if (!strcmp(argv[c], "-q") && c + 1 < argc) {
      quality = atof(argv[++c]);
      use_reduction = false;
      if (quality < 0 || quality > 100) {
        fprintf(stdout, "Error: invalid range for option '%s': %s\n",
                argv[c - 1], argv[c]);
        return 1;
      }
    } else if (!strcmp(argv[c], "-r") && c + 1 < argc) {
      reduction = atof(argv[++c]);
      use_reduction = true;
      if (reduction <= 0 || reduction > 100) {
        fprintf(stdout, "Error: invalid range for option '%s': %s\n",
                argv[c - 1], argv[c]);
        return 1;
      }
    } else if (!strcmp(argv[c], "-estimate")) {
      estimate = true;
    } else if (!strcmp(argv[c], "-no_limit")) {
      limit_quantization = false;
    } else if (!strcmp(argv[c], "-no_adapt")) {
      param.adaptive_quantization = false;
    } else if (!strcmp(argv[c], "-no_optim")) {
      param.Huffman_compress = false;
    } else if (!strcmp(argv[c], "-adapt_bias")) {
      param.adaptive_bias = true;
    } else if (!strcmp(argv[c], "-trellis")) {
      param.use_trellis = true;
    } else if (!strcmp(argv[c], "-psnr") && c + 1 < argc) {
      param.target_mode = SjpegEncodeParam::TARGET_PSNR;
      param.target_value = atof(argv[++c]);
    } else if (!strcmp(argv[c], "-tolerance")) {
      param.tolerance = atof(argv[++c]);
    } else if (!strcmp(argv[c], "-qmin")) {
      param.qmin = atof(argv[++c]);
    } else if (!strcmp(argv[c], "-qmax")) {
      param.qmax = atof(argv[++c]);
    } else if (!strcmp(argv[c], "-size") && c + 1 < argc) {
      param.target_mode = SjpegEncodeParam::TARGET_SIZE;
      param.target_value = atof(argv[++c]);
    } else if (!strcmp(argv[c], "-pass") && c + 1 < argc) {
      param.passes = atoi(argv[++c]);
    } else if (!strcmp(argv[c], "-no_metadata")) {
      no_metadata = true;
    } else if (!strcmp(argv[c], "-yuv_mode") && c + 1 < argc) {
      const int mode = atoi(argv[++c]);
      if (mode < 0 || mode > 3) {
        fprintf(stdout, "Error: invalid range for option '%s': %s\n",
                argv[c - 1], argv[c]);
        return 1;
      }
      param.yuv_mode = (SjpegYUVMode)mode;
    } else if (!strcmp(argv[c], "-444")) {
      param.yuv_mode = SJPEG_YUV_444;
    } else if (!strcmp(argv[c], "-sharp")) {
      param.yuv_mode = SJPEG_YUV_SHARP;
    } else if (!strcmp(argv[c], "-420")) {
      param.yuv_mode = SJPEG_YUV_420;
    } else if (!strcmp(argv[c], "-i") || !strcmp(argv[c], "-info")) {
      info = true;
    } else if (!strcmp(argv[c], "-quiet")) {
      quiet = true;
    } else if (!strcmp(argv[c], "-short")) {
      short_output = true;
    } else if (!strcmp(argv[c], "-version")) {
      const uint32_t version = SjpegVersion();
      fprintf(stdout, "%d.%d.%d\n",
              (version >> 16) & 0xff,
              (version >>  8) & 0xff,
              (version >>  0) & 0xff);
      return 0;
    } else {
      input_file = argv[c];
    }
  }
  if (input_file == NULL) {
    fprintf(stderr, "Missing input file.\n");
    if (!quiet) fprintf(stderr, usage);
    return -1;
  }
  // finish param set up
  if (param.target_mode != SjpegEncodeParam::TARGET_NONE && param.passes <= 1) {
    param.passes = 10;
  }
  // Read input file into the buffer in_bytes[]
  std::string input = ReadFile(input_file);
  if (input.size() == 0) return 1;

  const ImageType input_type = GuessImageType(input);
  uint8_t quant_matrices[2][64];
  const int nb_matrices =
    (input_type == SJPEG_JPEG) ? SjpegFindQuantizer(input, quant_matrices)
                               : 0;
  const bool is_jpeg = (input_type == SJPEG_JPEG) && (nb_matrices > 0);
  if (use_reduction && !is_jpeg) {
    if (!quiet && !short_output) {
      fprintf(stdout, "Warning! reduction factor (-r option) disabled"
                      " (only applies to JPEG source).\n");
      fprintf(stdout, "         Please use the -q option to set the"
                      " quality factor.\n\n");
    }
    use_reduction = false;
  }
  if (use_reduction) {   // use 'reduction' factor for JPEG source
    param.SetQuantMatrix(quant_matrices[0], 0, reduction);
    param.SetQuantMatrix(quant_matrices[1], 1, reduction);
    param.SetLimitQuantization(true);
  } else {    // the '-q' option has been used.
    param.SetQuality(quality);
    param.SetLimitQuantization(false);
  }

  if (estimate) {
    const int q = is_jpeg ? SjpegEstimateQuality(quant_matrices[0], 0) : 100;
    fprintf(stdout, "%d\n", q);
    return 0;
  }
  int W, H;
  vector<uint8_t> in_bytes = ReadImage(input, &W, &H, &param);
  if (in_bytes.size() == 0) return 1;

  if (!short_output && !quiet) {
    fprintf(stdout, "Input [%s]: %s (%ld bytes, %d x %d)\n",
            ImageTypeName(input_type), input_file,
            static_cast<long>(input.size()),
            W, H);
    if (info) {
      yuv_mode_rec = SjpegRiskiness(&in_bytes[0], W, H, 3 * W, &riskiness);
      fprintf(stdout, "Riskiness:   %.1f (recommended yuv_mode: %s)\n",
              riskiness, kYUVModeNames[yuv_mode_rec]);

      if (is_jpeg) {
        fprintf(stdout, "Input is JPEG w/ %d matrices:\n", nb_matrices);
        if (nb_matrices > 0) {
          PrintMatrix("Luma", quant_matrices[0], false);
        }
        if (nb_matrices > 1) {
          PrintMatrix("UV-chroma", quant_matrices[1], true);
        }
      }
    }
  }
  if (info) return 0;   // done

  // finish setting up the quantization matrices
  if (limit_quantization == false) param.SetLimitQuantization(false);

  if (no_metadata) param.ResetMetadata();

  const double start = GetStopwatchTime();
  const std::string out = SjpegEncode(&in_bytes[0], W, H, 3 * W, param);
  const double encode_time = GetStopwatchTime() - start;

  if (out.size() == 0) {
    fprintf(stderr, "ERROR: call to SjpegEncode() failed.\n");
    return -1;
  }

  if (param.target_mode != SjpegEncodeParam::TARGET_NONE &&
      param.search_hook != nullptr) {
    quality = param.search_hook->q;  // retrieve the final quality used
  }

  if (!short_output && !quiet) {
    yuv_mode_rec = SjpegRiskiness(&in_bytes[0], W, H, 3 * W, &riskiness);
    fprintf(stdout, "new size:   %ld bytes (%.2lf%% of original)\n"
                    "%s%.1f (adaptive: %s, Huffman: %s)\n"
                    "yuv mode:   %s (riskiness: %.1lf%%)\n"
                    "elapsed:    %d ms\n",
                    (long)out.size(), 100. * out.size() / input.size(),
                    use_reduction ? "reduction:  r=" : "quality:    q=",
                    use_reduction ? reduction : quality,
                    kNoYes[param.adaptive_quantization],
                    kNoYes[param.Huffman_compress],
                    kYUVModeNames[yuv_mode_rec], riskiness,
                    static_cast<int>(1000. * encode_time));
    if (param.iccp.size()) {
      fprintf(stdout, "ICCP:       %ld bytes\n", (long)param.iccp.size());
    }
    if (param.exif.size()) {
      fprintf(stdout, "EXIF:       %ld bytes\n", (long)param.exif.size());
    }
    if (param.xmp.size()) {
      fprintf(stdout, "XMP:        %ld bytes\n", (long)param.xmp.size());
    }
  } else if (!quiet) {
    fprintf(stdout, "%ld %ld %.2lf %%\n",
           static_cast<long>(input.size()), static_cast<long>(out.size()),
           100. * out.size() / input.size());
  }

  // Save the result.
  if (!SaveFile(output_file, out, quiet)) return 1;

  return 0;     // ok.
}

///////////////////////////////////////////////////////////////////////////////
<|endoftext|>