<commit_before>/***************************************************************************
 *                                                                         *
 * Copyright (C) 2007-2015 by frePPLe bvba                                 *
 *                                                                         *
 * This library is free software; you can redistribute it and/or modify it *
 * under the terms of the GNU Affero General Public License as published   *
 * by the Free Software Foundation; either version 3 of the License, or    *
 * (at your option) any later version.                                     *
 *                                                                         *
 * This library is distributed in the hope that it will be useful,         *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            *
 * GNU Affero General Public License for more details.                     *
 *                                                                         *
 * You should have received a copy of the GNU Affero General Public        *
 * License along with this program.                                        *
 * If not, see <http://www.gnu.org/licenses/>.                             *
 *                                                                         *
 ***************************************************************************/

#define FREPPLE_CORE
#include "frepple/model.h"
namespace frepple {

const MetaClass* FlowPlan::metadata;
const MetaCategory* FlowPlan::metacategory;

int FlowPlan::initialize() {
  // Initialize the metadata
  metacategory =
      MetaCategory::registerCategory<FlowPlan>("flowplan", "flowplans", reader);
  metadata = MetaClass::registerClass<FlowPlan>("flowplan", "flowplan");
  registerFields<FlowPlan>(const_cast<MetaClass*>(metadata));

  // Initialize the Python type
  PythonType& x = FreppleCategory<FlowPlan>::getPythonType();
  x.setName("flowplan");
  x.setDoc("frePPLe flowplan");
  x.supportgetattro();
  x.supportsetattro();
  const_cast<MetaClass*>(metadata)->pythonClass = x.type_object();
  return x.typeReady();
}

FlowPlan::FlowPlan(OperationPlan* opplan, const Flow* f)
    : fl(const_cast<Flow*>(f)), oper(opplan) {
  assert(opplan && f);

  // Initialize the Python type
  initType(metadata);

  // Link the flowplan to the operationplan
  if (opplan->firstflowplan) {
    // Append to the end
    FlowPlan* c = opplan->firstflowplan;
    while (c->nextFlowPlan) c = c->nextFlowPlan;
    c->nextFlowPlan = this;
  } else
    // First in the list
    opplan->firstflowplan = this;

  // Find the buffer
  if (fl->getBuffer() && fl->getBuffer()->getItem() &&
      fl->getBuffer()->getItem()->hasType<ItemMTO>()) {
    buf = Buffer::findOrCreate(fl->getBuffer()->getItem(),
                               fl->getBuffer()->getLocation(),
                               opplan->getBatch());
  } else
    buf = fl->getBuffer();
  assert(buf);

  // Compute the flowplan quantity
  auto fl_info = fl->getFlowplanDateQuantity(this);
  buf->flowplans.insert(this, fl_info.second, fl_info.first);

  // Mark the operation and buffer as having changed. This will trigger the
  // recomputation of their problems
  buf->setChanged();
  fl->getOperation()->setChanged();
}

FlowPlan::FlowPlan(OperationPlan* opplan, const Flow* f, Date d, double q)
    : fl(const_cast<Flow*>(f)), oper(opplan) {
  assert(opplan && f);

  // Initialize the Python type
  initType(metadata);

  // Link the flowplan to the operationplan
  if (opplan->firstflowplan) {
    // Append to the end
    FlowPlan* c = opplan->firstflowplan;
    while (c->nextFlowPlan) c = c->nextFlowPlan;
    c->nextFlowPlan = this;
  } else
    // First in the list
    opplan->firstflowplan = this;

  // Find the buffer
  if (fl->getBuffer() && fl->getBuffer()->getItem() &&
      fl->getBuffer()->getItem()->hasType<ItemMTO>()) {
    if (fl->getBuffer()->getItem()->hasType<ItemMTO>() && opplan->getBatch())
      buf = Buffer::findOrCreate(fl->getBuffer()->getItem(),
                                 fl->getBuffer()->getLocation(),
                                 opplan->getBatch());
  } else
    buf = fl->getBuffer();
  assert(buf);

  // Compute the flowplan quantity
  buf->flowplans.insert(this, q, d);

  // Mark the operation and buffer as having changed. This will trigger the
  // recomputation of their problems
  buf->setChanged();
  fl->getOperation()->setChanged();
}

string FlowPlan::getStatus() const {
  if (flags & STATUS_CONFIRMED)
    return "confirmed";
  else
    return "proposed";
}

void FlowPlan::setStatus(const string& s) {
  if (getOperationPlan()->getProposed() && s == "confirmed")
    throw DataException(
        "OperationPlanMaterial locked while OperationPlan is not");
  if (s == "confirmed")
    flags |= STATUS_CONFIRMED;
  else if (s == "proposed")
    flags &= ~STATUS_CONFIRMED;
  else
    throw DataException("invalid operationplanmaterial status:" + s);
}

void FlowPlan::update() {
  // Update the timeline data structure
  auto fl_info = fl->getFlowplanDateQuantity(this);
  buf->flowplans.update(this, fl_info.second, fl_info.first);

  // Mark the operation and buffer as having changed. This will trigger the
  // recomputation of their problems
  buf->setChanged();
  fl->getOperation()->setChanged();
}

void FlowPlan::updateBatch() {
  // Remove from the old buffer, if there is one
  if (buf) {
    buf->flowplans.erase(this);
    buf->setChanged();
    buf->flowplans.check();
  }

  // Insert in the new buffer
  PooledString batch = getOperationPlan()->getBatch();
  if (fl->getBuffer()->getItem()->hasType<ItemMTO>() && batch)
    buf = Buffer::findOrCreate(fl->getBuffer()->getItem(),
                               fl->getBuffer()->getLocation(), batch);
  else
    buf = fl->getBuffer();
  buf->flowplans.insert(this, getQuantity(), getDate());
  buf->setChanged();
  buf->flowplans.check();
}

void FlowPlan::setFlow(Flow* newfl) {
  // No change
  if (newfl == fl) return;

  // Verify the data
  if (!newfl) throw DataException("Can't switch to nullptr flow");
  if (newfl->getType() != fl->getType())
    throw DataException("Flowplans can only switch to flows of the same type");

  PooledString batch;
  if (buf) batch = buf->getBatch();
  if (!newfl->hasType<FlowTransferBatch>() || !fl) {
    // Remove from the old buffer, if there is one
    if (fl) {
      if (fl->getOperation() != newfl->getOperation())
        throw DataException(
            "Only switching to a flow on the same operation is allowed");
      buf->flowplans.erase(this);
      buf->setChanged();
    }

    // Insert in the new buffer
    fl = newfl;
    auto fl_info = fl->getFlowplanDateQuantity(this);
    if (fl->getBuffer()->getItem()->hasType<ItemMTO>() && !batch.empty())
      buf = Buffer::findOrCreate(fl->getBuffer()->getItem(),
                                 fl->getBuffer()->getLocation(), batch);
    else
      buf = fl->getBuffer();
    buf->flowplans.insert(this, fl_info.second, fl_info.first);
    buf->setChanged();
    fl->getOperation()->setChanged();
  } else {
    // Switch all flowplans of the same transfer batch
    auto oldFlow = fl;
    if (oldFlow->getOperation() != newfl->getOperation())
      throw DataException(
          "Only switching to a flow on the same operation is allowed");
    if (fl->getBuffer()->getItem()->hasType<ItemMTO>() && !batch.empty())
      buf = Buffer::findOrCreate(fl->getBuffer()->getItem(),
                                 fl->getBuffer()->getLocation(), batch);
    else
      buf = fl->getBuffer();
    for (auto flpln = getOperationPlan()->beginFlowPlans();
         flpln != getOperationPlan()->endFlowPlans(); ++flpln) {
      if (flpln->getFlow() != oldFlow) continue;

      // Remove from the old buffer
      flpln->buf->flowplans.erase(&*flpln);
      flpln->buf->setChanged();

      // Insert in the new buffer
      flpln->fl = newfl;
      auto fl_info = flpln->fl->getFlowplanDateQuantity(&*flpln);
      buf->flowplans.insert(&*flpln, fl_info.second, fl_info.first);
      buf->setChanged();
      flpln->fl->getOperation()->setChanged();
    }
  }
}

void FlowPlan::setItem(Item* newItem) {
  // Verify the data
  if (!newItem) throw DataException("Can't switch to nullptr flow");

  if (fl && fl->getBuffer()) {
    if (newItem == fl->getBuffer()->getItem())
      // No change
      return;
    else
      // Already set
      throw DataException("Item can be set only once on a flowplan");
  }

  // We are not expecting to use this method in this way...
  throw LogicException("Not implemented");
}

pair<double, double> FlowPlan::setQuantity(double quantity, bool rounddown,
                                           bool update, bool execute,
                                           short mode) {
  // TODO argument "update" isn't used
  if (getConfirmed()) {
    // Confirmed flowplans take any quantity, regardless of the
    // quantity of the owning operationplan.
    if (execute) {
      // Update the timeline data structure
      buf->flowplans.update(this, quantity, getDate());

      // Mark the operation and buffer as having changed. This will trigger the
      // recomputation of their problems
      buf->setChanged();
      fl->getOperation()->setChanged();
    }
    return make_pair(quantity, oper->getQuantity());
  }

  if (!getFlow()->getEffective().within(getDate())) {
    if (execute) {
      if (mode == 2 || (mode == 0 && getFlow()->hasType<FlowEnd>())) {
        oper->setOperationPlanParameters(
            0.0, Date::infinitePast, computeFlowToOperationDate(oper->getEnd()),
            true, execute, rounddown);
      } else if (mode == 1 || (mode == 0 && getFlow()->hasType<FlowStart>())) {
        oper->setOperationPlanParameters(
            0.0, computeFlowToOperationDate(oper->getStart()),
            Date::infinitePast, false, execute, rounddown);
      }
    }
    return make_pair(0.0, 0.0);
  }

  double opplan_quantity;
  bool less_than_fixed_qty =
      fabs(quantity) < fabs(getFlow()->getQuantityFixed()) + ROUNDING_ERROR;
  if (getFlow()->getQuantity() == 0.0 || less_than_fixed_qty) {
    // Fixed quantity flows only allow resizing to 0
    if (less_than_fixed_qty && oper->getQuantity() != 0.0) {
      if (mode == 2 || (mode == 0 && getFlow()->hasType<FlowEnd>()))
        opplan_quantity = oper->setOperationPlanParameters(
                                  0.0, Date::infinitePast,
                                  computeFlowToOperationDate(oper->getEnd()),
                                  true, execute, rounddown)
                              .quantity;
      else if (mode == 1 || (mode == 0 && getFlow()->hasType<FlowStart>()))
        opplan_quantity =
            oper->setOperationPlanParameters(
                    0.0, computeFlowToOperationDate(oper->getStart()),
                    Date::infinitePast, false, execute, rounddown)
                .quantity;
      else
        throw LogicException("Unreachable code reached");
    } else if (!less_than_fixed_qty && oper->getQuantity() == 0.0) {
      if (mode == 2 || (mode == 0 && getFlow()->hasType<FlowEnd>()))
        opplan_quantity = oper->setOperationPlanParameters(
                                  0.001, Date::infinitePast,
                                  computeFlowToOperationDate(oper->getEnd()),
                                  true, execute, rounddown)
                              .quantity;
      else if (mode == 1 || (mode == 0 && getFlow()->hasType<FlowStart>()))
        opplan_quantity =
            oper->setOperationPlanParameters(
                    0.001, computeFlowToOperationDate(oper->getStart()),
                    Date::infinitePast, false, execute, rounddown)
                .quantity;
      else
        throw LogicException("Unreachable code reached");
    }
  } else {
    // Proportional or transfer batch flows
    // For transfer batch flowplans the argument quantity is expected to be the
    // total quantity of all batches.
    if (mode == 2 || (mode == 0 && getFlow()->hasType<FlowEnd>()))
      opplan_quantity = oper->setOperationPlanParameters(
                                (quantity - getFlow()->getQuantityFixed()) /
                                    getFlow()->getQuantity(),
                                Date::infinitePast,
                                computeFlowToOperationDate(oper->getEnd()),
                                true, execute, rounddown)
                            .quantity;
    else if (mode == 1 || (mode == 0 && getFlow()->hasType<FlowStart>()))
      opplan_quantity = oper->setOperationPlanParameters(
                                (quantity - getFlow()->getQuantityFixed()) /
                                    getFlow()->getQuantity(),
                                computeFlowToOperationDate(oper->getStart()),
                                Date::infinitePast, false, execute, rounddown)
                            .quantity;
    else
      throw LogicException("Unreachable code reached");
  }
  if (opplan_quantity)
    return make_pair(opplan_quantity * getFlow()->getQuantity() +
                         getFlow()->getQuantityFixed(),
                     opplan_quantity);
  else
    return make_pair(0.0, 0.0);
}

int FlowPlanIterator::initialize() {
  // Initialize the type
  PythonType& x = PythonExtension<FlowPlanIterator>::getPythonType();
  x.setName("flowplanIterator");
  x.setDoc("frePPLe iterator for flowplan");
  x.supportiter();
  return x.typeReady();
}

PyObject* FlowPlanIterator::iternext() {
  FlowPlan* fl;
  if (buffer_or_opplan) {
    // Skip uninteresting entries
    while (*bufiter != buf->getFlowPlans().end() &&
           (*bufiter)->getQuantity() == 0.0)
      ++(*bufiter);
    if (*bufiter == buf->getFlowPlans().end()) return nullptr;
    fl = const_cast<FlowPlan*>(static_cast<const FlowPlan*>(&*((*bufiter)++)));
  } else {
    // Skip uninteresting entries
    while (*opplaniter != opplan->endFlowPlans() &&
           (*opplaniter)->getQuantity() == 0.0)
      ++(*opplaniter);
    if (*opplaniter == opplan->endFlowPlans()) return nullptr;
    fl = static_cast<FlowPlan*>(&*((*opplaniter)++));
  }
  Py_INCREF(fl);
  return const_cast<FlowPlan*>(fl);
}

Object* FlowPlan::reader(const MetaClass* cat, const DataValueDict& in,
                         CommandManager* mgr) {
  // Pick up the operationplan attribute. An error is reported if it's missing.
  const DataValue* opplanElement = in.get(Tags::operationplan);
  if (!opplanElement) throw DataException("Missing operationplan field");
  Object* opplanobject = opplanElement->getObject();
  if (!opplanobject || !opplanobject->hasType<OperationPlan>())
    throw DataException("Invalid operationplan field");
  OperationPlan* opplan = static_cast<OperationPlan*>(opplanobject);

  // Pick up the item.
  const DataValue* itemElement = in.get(Tags::item);
  if (!itemElement) throw DataException("Item must be provided");
  Object* itemobject = itemElement->getObject();
  if (!itemobject || itemobject->getType().category != Item::metadata)
    throw DataException("Invalid item field");
  Item* itm = static_cast<Item*>(itemobject);

  // Find the flow for this item on the operationplan.
  // If multiple exist, we pick up the first one.
  // If none is found, we throw a data error.
  // TODO detect situations where the flowplan is on an alternate material
  auto flplniter = opplan->getFlowPlans();
  FlowPlan* flpln;
  while ((flpln = flplniter.next())) {
    if (flpln->getItem() == itm) return flpln;
  }
  return nullptr;
}

Duration FlowPlan::getPeriodOfCover() const {
  double left_for_consumption = getOnhand();
  if (left_for_consumption < ROUNDING_ERROR) return Duration(0L);
  auto fpiter = getBuffer()->getFlowPlans().begin(this);
  ++fpiter;
  while (fpiter != getBuffer()->getFlowPlans().end()) {
    if (fpiter->getQuantity() < 0.0) {
      left_for_consumption += fpiter->getQuantity();
      if (left_for_consumption < ROUNDING_ERROR)
        return fpiter->getDate() - getDate();
    }
    ++fpiter;
  }
  return Duration(999L * 86400L);
}

}  // namespace frepple
<commit_msg>remove debugging checks<commit_after>/***************************************************************************
 *                                                                         *
 * Copyright (C) 2007-2015 by frePPLe bvba                                 *
 *                                                                         *
 * This library is free software; you can redistribute it and/or modify it *
 * under the terms of the GNU Affero General Public License as published   *
 * by the Free Software Foundation; either version 3 of the License, or    *
 * (at your option) any later version.                                     *
 *                                                                         *
 * This library is distributed in the hope that it will be useful,         *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            *
 * GNU Affero General Public License for more details.                     *
 *                                                                         *
 * You should have received a copy of the GNU Affero General Public        *
 * License along with this program.                                        *
 * If not, see <http://www.gnu.org/licenses/>.                             *
 *                                                                         *
 ***************************************************************************/

#define FREPPLE_CORE
#include "frepple/model.h"
namespace frepple {

const MetaClass* FlowPlan::metadata;
const MetaCategory* FlowPlan::metacategory;

int FlowPlan::initialize() {
  // Initialize the metadata
  metacategory =
      MetaCategory::registerCategory<FlowPlan>("flowplan", "flowplans", reader);
  metadata = MetaClass::registerClass<FlowPlan>("flowplan", "flowplan");
  registerFields<FlowPlan>(const_cast<MetaClass*>(metadata));

  // Initialize the Python type
  PythonType& x = FreppleCategory<FlowPlan>::getPythonType();
  x.setName("flowplan");
  x.setDoc("frePPLe flowplan");
  x.supportgetattro();
  x.supportsetattro();
  const_cast<MetaClass*>(metadata)->pythonClass = x.type_object();
  return x.typeReady();
}

FlowPlan::FlowPlan(OperationPlan* opplan, const Flow* f)
    : fl(const_cast<Flow*>(f)), oper(opplan) {
  assert(opplan && f);

  // Initialize the Python type
  initType(metadata);

  // Link the flowplan to the operationplan
  if (opplan->firstflowplan) {
    // Append to the end
    FlowPlan* c = opplan->firstflowplan;
    while (c->nextFlowPlan) c = c->nextFlowPlan;
    c->nextFlowPlan = this;
  } else
    // First in the list
    opplan->firstflowplan = this;

  // Find the buffer
  if (fl->getBuffer() && fl->getBuffer()->getItem() &&
      fl->getBuffer()->getItem()->hasType<ItemMTO>()) {
    buf = Buffer::findOrCreate(fl->getBuffer()->getItem(),
                               fl->getBuffer()->getLocation(),
                               opplan->getBatch());
  } else
    buf = fl->getBuffer();
  assert(buf);

  // Compute the flowplan quantity
  auto fl_info = fl->getFlowplanDateQuantity(this);
  buf->flowplans.insert(this, fl_info.second, fl_info.first);

  // Mark the operation and buffer as having changed. This will trigger the
  // recomputation of their problems
  buf->setChanged();
  fl->getOperation()->setChanged();
}

FlowPlan::FlowPlan(OperationPlan* opplan, const Flow* f, Date d, double q)
    : fl(const_cast<Flow*>(f)), oper(opplan) {
  assert(opplan && f);

  // Initialize the Python type
  initType(metadata);

  // Link the flowplan to the operationplan
  if (opplan->firstflowplan) {
    // Append to the end
    FlowPlan* c = opplan->firstflowplan;
    while (c->nextFlowPlan) c = c->nextFlowPlan;
    c->nextFlowPlan = this;
  } else
    // First in the list
    opplan->firstflowplan = this;

  // Find the buffer
  if (fl->getBuffer() && fl->getBuffer()->getItem() &&
      fl->getBuffer()->getItem()->hasType<ItemMTO>()) {
    if (fl->getBuffer()->getItem()->hasType<ItemMTO>() && opplan->getBatch())
      buf = Buffer::findOrCreate(fl->getBuffer()->getItem(),
                                 fl->getBuffer()->getLocation(),
                                 opplan->getBatch());
  } else
    buf = fl->getBuffer();
  assert(buf);

  // Compute the flowplan quantity
  buf->flowplans.insert(this, q, d);

  // Mark the operation and buffer as having changed. This will trigger the
  // recomputation of their problems
  buf->setChanged();
  fl->getOperation()->setChanged();
}

string FlowPlan::getStatus() const {
  if (flags & STATUS_CONFIRMED)
    return "confirmed";
  else
    return "proposed";
}

void FlowPlan::setStatus(const string& s) {
  if (getOperationPlan()->getProposed() && s == "confirmed")
    throw DataException(
        "OperationPlanMaterial locked while OperationPlan is not");
  if (s == "confirmed")
    flags |= STATUS_CONFIRMED;
  else if (s == "proposed")
    flags &= ~STATUS_CONFIRMED;
  else
    throw DataException("invalid operationplanmaterial status:" + s);
}

void FlowPlan::update() {
  // Update the timeline data structure
  auto fl_info = fl->getFlowplanDateQuantity(this);
  buf->flowplans.update(this, fl_info.second, fl_info.first);

  // Mark the operation and buffer as having changed. This will trigger the
  // recomputation of their problems
  buf->setChanged();
  fl->getOperation()->setChanged();
}

void FlowPlan::updateBatch() {
  // Remove from the old buffer, if there is one
  if (buf) {
    buf->flowplans.erase(this);
    buf->setChanged();
  }

  // Insert in the new buffer
  PooledString batch = getOperationPlan()->getBatch();
  if (fl->getBuffer()->getItem()->hasType<ItemMTO>() && batch)
    buf = Buffer::findOrCreate(fl->getBuffer()->getItem(),
                               fl->getBuffer()->getLocation(), batch);
  else
    buf = fl->getBuffer();
  buf->flowplans.insert(this, getQuantity(), getDate());
  buf->setChanged();
}

void FlowPlan::setFlow(Flow* newfl) {
  // No change
  if (newfl == fl) return;

  // Verify the data
  if (!newfl) throw DataException("Can't switch to nullptr flow");
  if (newfl->getType() != fl->getType())
    throw DataException("Flowplans can only switch to flows of the same type");

  PooledString batch;
  if (buf) batch = buf->getBatch();
  if (!newfl->hasType<FlowTransferBatch>() || !fl) {
    // Remove from the old buffer, if there is one
    if (fl) {
      if (fl->getOperation() != newfl->getOperation())
        throw DataException(
            "Only switching to a flow on the same operation is allowed");
      buf->flowplans.erase(this);
      buf->setChanged();
    }

    // Insert in the new buffer
    fl = newfl;
    auto fl_info = fl->getFlowplanDateQuantity(this);
    if (fl->getBuffer()->getItem()->hasType<ItemMTO>() && !batch.empty())
      buf = Buffer::findOrCreate(fl->getBuffer()->getItem(),
                                 fl->getBuffer()->getLocation(), batch);
    else
      buf = fl->getBuffer();
    buf->flowplans.insert(this, fl_info.second, fl_info.first);
    buf->setChanged();
    fl->getOperation()->setChanged();
  } else {
    // Switch all flowplans of the same transfer batch
    auto oldFlow = fl;
    if (oldFlow->getOperation() != newfl->getOperation())
      throw DataException(
          "Only switching to a flow on the same operation is allowed");
    if (fl->getBuffer()->getItem()->hasType<ItemMTO>() && !batch.empty())
      buf = Buffer::findOrCreate(fl->getBuffer()->getItem(),
                                 fl->getBuffer()->getLocation(), batch);
    else
      buf = fl->getBuffer();
    for (auto flpln = getOperationPlan()->beginFlowPlans();
         flpln != getOperationPlan()->endFlowPlans(); ++flpln) {
      if (flpln->getFlow() != oldFlow) continue;

      // Remove from the old buffer
      flpln->buf->flowplans.erase(&*flpln);
      flpln->buf->setChanged();

      // Insert in the new buffer
      flpln->fl = newfl;
      auto fl_info = flpln->fl->getFlowplanDateQuantity(&*flpln);
      buf->flowplans.insert(&*flpln, fl_info.second, fl_info.first);
      buf->setChanged();
      flpln->fl->getOperation()->setChanged();
    }
  }
}

void FlowPlan::setItem(Item* newItem) {
  // Verify the data
  if (!newItem) throw DataException("Can't switch to nullptr flow");

  if (fl && fl->getBuffer()) {
    if (newItem == fl->getBuffer()->getItem())
      // No change
      return;
    else
      // Already set
      throw DataException("Item can be set only once on a flowplan");
  }

  // We are not expecting to use this method in this way...
  throw LogicException("Not implemented");
}

pair<double, double> FlowPlan::setQuantity(double quantity, bool rounddown,
                                           bool update, bool execute,
                                           short mode) {
  // TODO argument "update" isn't used
  if (getConfirmed()) {
    // Confirmed flowplans take any quantity, regardless of the
    // quantity of the owning operationplan.
    if (execute) {
      // Update the timeline data structure
      buf->flowplans.update(this, quantity, getDate());

      // Mark the operation and buffer as having changed. This will trigger the
      // recomputation of their problems
      buf->setChanged();
      fl->getOperation()->setChanged();
    }
    return make_pair(quantity, oper->getQuantity());
  }

  if (!getFlow()->getEffective().within(getDate())) {
    if (execute) {
      if (mode == 2 || (mode == 0 && getFlow()->hasType<FlowEnd>())) {
        oper->setOperationPlanParameters(
            0.0, Date::infinitePast, computeFlowToOperationDate(oper->getEnd()),
            true, execute, rounddown);
      } else if (mode == 1 || (mode == 0 && getFlow()->hasType<FlowStart>())) {
        oper->setOperationPlanParameters(
            0.0, computeFlowToOperationDate(oper->getStart()),
            Date::infinitePast, false, execute, rounddown);
      }
    }
    return make_pair(0.0, 0.0);
  }

  double opplan_quantity;
  bool less_than_fixed_qty =
      fabs(quantity) < fabs(getFlow()->getQuantityFixed()) + ROUNDING_ERROR;
  if (getFlow()->getQuantity() == 0.0 || less_than_fixed_qty) {
    // Fixed quantity flows only allow resizing to 0
    if (less_than_fixed_qty && oper->getQuantity() != 0.0) {
      if (mode == 2 || (mode == 0 && getFlow()->hasType<FlowEnd>()))
        opplan_quantity = oper->setOperationPlanParameters(
                                  0.0, Date::infinitePast,
                                  computeFlowToOperationDate(oper->getEnd()),
                                  true, execute, rounddown)
                              .quantity;
      else if (mode == 1 || (mode == 0 && getFlow()->hasType<FlowStart>()))
        opplan_quantity =
            oper->setOperationPlanParameters(
                    0.0, computeFlowToOperationDate(oper->getStart()),
                    Date::infinitePast, false, execute, rounddown)
                .quantity;
      else
        throw LogicException("Unreachable code reached");
    } else if (!less_than_fixed_qty && oper->getQuantity() == 0.0) {
      if (mode == 2 || (mode == 0 && getFlow()->hasType<FlowEnd>()))
        opplan_quantity = oper->setOperationPlanParameters(
                                  0.001, Date::infinitePast,
                                  computeFlowToOperationDate(oper->getEnd()),
                                  true, execute, rounddown)
                              .quantity;
      else if (mode == 1 || (mode == 0 && getFlow()->hasType<FlowStart>()))
        opplan_quantity =
            oper->setOperationPlanParameters(
                    0.001, computeFlowToOperationDate(oper->getStart()),
                    Date::infinitePast, false, execute, rounddown)
                .quantity;
      else
        throw LogicException("Unreachable code reached");
    }
  } else {
    // Proportional or transfer batch flows
    // For transfer batch flowplans the argument quantity is expected to be the
    // total quantity of all batches.
    if (mode == 2 || (mode == 0 && getFlow()->hasType<FlowEnd>()))
      opplan_quantity = oper->setOperationPlanParameters(
                                (quantity - getFlow()->getQuantityFixed()) /
                                    getFlow()->getQuantity(),
                                Date::infinitePast,
                                computeFlowToOperationDate(oper->getEnd()),
                                true, execute, rounddown)
                            .quantity;
    else if (mode == 1 || (mode == 0 && getFlow()->hasType<FlowStart>()))
      opplan_quantity = oper->setOperationPlanParameters(
                                (quantity - getFlow()->getQuantityFixed()) /
                                    getFlow()->getQuantity(),
                                computeFlowToOperationDate(oper->getStart()),
                                Date::infinitePast, false, execute, rounddown)
                            .quantity;
    else
      throw LogicException("Unreachable code reached");
  }
  if (opplan_quantity)
    return make_pair(opplan_quantity * getFlow()->getQuantity() +
                         getFlow()->getQuantityFixed(),
                     opplan_quantity);
  else
    return make_pair(0.0, 0.0);
}

int FlowPlanIterator::initialize() {
  // Initialize the type
  PythonType& x = PythonExtension<FlowPlanIterator>::getPythonType();
  x.setName("flowplanIterator");
  x.setDoc("frePPLe iterator for flowplan");
  x.supportiter();
  return x.typeReady();
}

PyObject* FlowPlanIterator::iternext() {
  FlowPlan* fl;
  if (buffer_or_opplan) {
    // Skip uninteresting entries
    while (*bufiter != buf->getFlowPlans().end() &&
           (*bufiter)->getQuantity() == 0.0)
      ++(*bufiter);
    if (*bufiter == buf->getFlowPlans().end()) return nullptr;
    fl = const_cast<FlowPlan*>(static_cast<const FlowPlan*>(&*((*bufiter)++)));
  } else {
    // Skip uninteresting entries
    while (*opplaniter != opplan->endFlowPlans() &&
           (*opplaniter)->getQuantity() == 0.0)
      ++(*opplaniter);
    if (*opplaniter == opplan->endFlowPlans()) return nullptr;
    fl = static_cast<FlowPlan*>(&*((*opplaniter)++));
  }
  Py_INCREF(fl);
  return const_cast<FlowPlan*>(fl);
}

Object* FlowPlan::reader(const MetaClass* cat, const DataValueDict& in,
                         CommandManager* mgr) {
  // Pick up the operationplan attribute. An error is reported if it's missing.
  const DataValue* opplanElement = in.get(Tags::operationplan);
  if (!opplanElement) throw DataException("Missing operationplan field");
  Object* opplanobject = opplanElement->getObject();
  if (!opplanobject || !opplanobject->hasType<OperationPlan>())
    throw DataException("Invalid operationplan field");
  OperationPlan* opplan = static_cast<OperationPlan*>(opplanobject);

  // Pick up the item.
  const DataValue* itemElement = in.get(Tags::item);
  if (!itemElement) throw DataException("Item must be provided");
  Object* itemobject = itemElement->getObject();
  if (!itemobject || itemobject->getType().category != Item::metadata)
    throw DataException("Invalid item field");
  Item* itm = static_cast<Item*>(itemobject);

  // Find the flow for this item on the operationplan.
  // If multiple exist, we pick up the first one.
  // If none is found, we throw a data error.
  // TODO detect situations where the flowplan is on an alternate material
  auto flplniter = opplan->getFlowPlans();
  FlowPlan* flpln;
  while ((flpln = flplniter.next())) {
    if (flpln->getItem() == itm) return flpln;
  }
  return nullptr;
}

Duration FlowPlan::getPeriodOfCover() const {
  double left_for_consumption = getOnhand();
  if (left_for_consumption < ROUNDING_ERROR) return Duration(0L);
  auto fpiter = getBuffer()->getFlowPlans().begin(this);
  ++fpiter;
  while (fpiter != getBuffer()->getFlowPlans().end()) {
    if (fpiter->getQuantity() < 0.0) {
      left_for_consumption += fpiter->getQuantity();
      if (left_for_consumption < ROUNDING_ERROR)
        return fpiter->getDate() - getDate();
    }
    ++fpiter;
  }
  return Duration(999L * 86400L);
}

}  // namespace frepple
<|endoftext|>


<commit_before>#pragma once

#include <map>
#include <list>
#include <memory>

#include "exception.hpp"

#include "widgets/widget.hpp"

#include "util/util.hpp"

namespace morda{

/**
 * @brief Inflater of GUI from STOB description.
 * This class is used to inflate GUI widget hierarchies from STOB descriptions.
 * The GUI can be described in STOB with the following basic rules:
 * A widget name should start with capital letter. A widget property should start
 * with small letter. Specific widgets define more detailed rules of their description.
 */
class inflater{
	friend class Morda;

	inflater();

private:
	std::map<std::string, std::function<std::shared_ptr<morda::Widget>(const puu::trees&)> > factories;

	const decltype(factories)::value_type::second_type& find_factory(const std::string& widget_name);

	void add_factory(std::string&& widget_name, decltype(factories)::value_type::second_type&& factory);

public:
	/**
	 * @brief Registers a new widget type.
	 * Use this function to associate some widget class with a name which can be used
	 * in STOB GUI description.
	 * @param widget_name - name of the widget as it appears in GUI script.
	 */
	template <class T_Widget> void register_widget(const std::string& widget_name){
		this->add_factory(
				std::string(widget_name),
				[](const puu::trees& desc) -> std::shared_ptr<morda::Widget> {
					return std::make_shared<T_Widget>(puu_to_stob(desc).get());
				}
			);
	}

	/**
	 * @brief Remove previously registered widget type.
	 * @param widget_name - widget name as it appears in GUI script.
	 * @return true if factory was successfully removed.
	 * @return false if the factory with given widget name was not found in the list of registered factories.
	 */
	bool unregister_widget(const std::string& widget_name)noexcept;

	/**
	 * @brief Create widgets hierarchy from GUI script.
	 * @param gui_script - GUI script to use.
	 * @return reference to the inflated widget.
	 */
	std::shared_ptr<widget> inflate(const puu::trees& gui_script);

	/**
	 * @brief Inflate widget and cast to specified type.
	 * Only the first widget from the STOB chain is returned.
	 * @param gui_script - gui script to inflate widget from.
	 * @return reference to the inflated widget.
	 */
	template <typename T> std::shared_ptr<T> inflate_as(const puu::trees& gui_script){
		return std::dynamic_pointer_cast<T>(this->inflate(gui_script));
	}

	//TODO: deprecated, remove
	std::shared_ptr<morda::Widget> inflate(const stob::Node& chain);

	/**
	 * @brief Create widgets hierarchy from GUI script.
	 * @param str - string containing GUI description.
	 * @return reference to the inflated widget.
	 */
	std::shared_ptr<widget> inflate(const char* str);

	/**
	 * @brief Inflate widget and cast to specified type.
	 * Only the first widget from the GUI script is returned.
	 * @param str - string of the GUI script.
	 * @return reference to the inflated widget.
	 */
	template <typename T> std::shared_ptr<T> inflate_as(const char* str){
		return std::dynamic_pointer_cast<T>(this->inflate(str));
	}

	/**
	 * @brief Inflate widget described in GUI script.
	 * @param fi - file interface to get the GUI script.
	 * @return reference to the inflated widget.
	 */
	std::shared_ptr<morda::Widget> inflate(const papki::File& fi);

private:
	struct widget_template{
		puu::tree templ;
		std::set<std::string> vars;
	};

	widget_template parse_template(const puu::trees& chain);

	std::vector<std::map<std::string, widget_template>> templates;

	const widget_template* find_template(const std::string& name)const;

	void push_templates(const puu::trees& chain);

	void pop_templates();

	// variable name - value mapping
	std::vector<std::map<std::string, puu::trees>> variables;

	const puu::trees* find_variable(const std::string& name)const;

	void push_variables(const puu::trees& trees);

	void pop_variables();

	void substitute_variables(puu::trees& to)const;

	void pushDefs(const puu::trees& chain);
	void popDefs();
};

}
<commit_msg>stuff<commit_after>#pragma once

#include <map>
#include <memory>

#include "exception.hpp"

#include "widgets/widget.hpp"

#include "util/util.hpp"

namespace morda{

/**
 * @brief Inflater of GUI from STOB description.
 * This class is used to inflate GUI widget hierarchies from STOB descriptions.
 * The GUI can be described in STOB with the following basic rules:
 * A widget name should start with capital letter. A widget property should start
 * with small letter. Specific widgets define more detailed rules of their description.
 */
class inflater{
	friend class Morda;

	inflater();

private:
	std::map<std::string, std::function<std::shared_ptr<morda::Widget>(const puu::trees&)> > factories;

	const decltype(factories)::value_type::second_type& find_factory(const std::string& widget_name);

	void add_factory(std::string&& widget_name, decltype(factories)::value_type::second_type&& factory);

public:
	/**
	 * @brief Registers a new widget type.
	 * Use this function to associate some widget class with a name which can be used
	 * in STOB GUI description.
	 * @param widget_name - name of the widget as it appears in GUI script.
	 */
	template <class T_Widget> void register_widget(const std::string& widget_name){
		this->add_factory(
				std::string(widget_name),
				[](const puu::trees& desc) -> std::shared_ptr<morda::Widget> {
					return std::make_shared<T_Widget>(puu_to_stob(desc).get());
				}
			);
	}

	/**
	 * @brief Remove previously registered widget type.
	 * @param widget_name - widget name as it appears in GUI script.
	 * @return true if factory was successfully removed.
	 * @return false if the factory with given widget name was not found in the list of registered factories.
	 */
	bool unregister_widget(const std::string& widget_name)noexcept;

	/**
	 * @brief Create widgets hierarchy from GUI script.
	 * @param gui_script - GUI script to use.
	 * @return reference to the inflated widget.
	 */
	std::shared_ptr<widget> inflate(const puu::trees& gui_script);

	/**
	 * @brief Inflate widget and cast to specified type.
	 * Only the first widget from the STOB chain is returned.
	 * @param gui_script - gui script to inflate widget from.
	 * @return reference to the inflated widget.
	 */
	template <typename T> std::shared_ptr<T> inflate_as(const puu::trees& gui_script){
		return std::dynamic_pointer_cast<T>(this->inflate(gui_script));
	}

	//TODO: deprecated, remove
	std::shared_ptr<morda::Widget> inflate(const stob::Node& chain);

	/**
	 * @brief Create widgets hierarchy from GUI script.
	 * @param str - string containing GUI description.
	 * @return reference to the inflated widget.
	 */
	std::shared_ptr<widget> inflate(const char* str);

	/**
	 * @brief Inflate widget and cast to specified type.
	 * Only the first widget from the GUI script is returned.
	 * @param str - string of the GUI script.
	 * @return reference to the inflated widget.
	 */
	template <typename T> std::shared_ptr<T> inflate_as(const char* str){
		return std::dynamic_pointer_cast<T>(this->inflate(str));
	}

	/**
	 * @brief Inflate widget described in GUI script.
	 * @param fi - file interface to get the GUI script.
	 * @return reference to the inflated widget.
	 */
	std::shared_ptr<morda::Widget> inflate(const papki::File& fi);

private:
	struct widget_template{
		puu::tree templ;
		std::set<std::string> vars;
	};

	widget_template parse_template(const puu::trees& chain);

	std::vector<std::map<std::string, widget_template>> templates;

	const widget_template* find_template(const std::string& name)const;

	void push_templates(const puu::trees& chain);

	void pop_templates();

	// variable name - value mapping
	std::vector<std::map<std::string, puu::trees>> variables;

	const puu::trees* find_variable(const std::string& name)const;

	void push_variables(const puu::trees& trees);

	void pop_variables();

	void substitute_variables(puu::trees& to)const;

	void pushDefs(const puu::trees& chain);
	void popDefs();
};

}
<|endoftext|>


<commit_before>///
/// @file  phi_mpi.cpp
/// @brief The PhiCache class calculates the partial sieve function
///        (a.k.a. Legendre-sum) using the recursive formula:
///        phi(x, a) = phi(x, a - 1) - phi(x / primes[a], a - 1).
///        phi(x, a) counts the numbers <= x that are not divisible by
///        any of the first a primes. The algorithm used is an
///        optimized version of the algorithm described in Tomás
///        Oliveira e Silva's paper [1]. I have added 5 optimizations
///        to my implementation which significantly speed up the
///        calculation:
///
///        * Cache results of phi(x, a)
///        * Calculate phi(x, a) using formula [2] if a <= 6
///        * Calculate phi(x, a) using pi(x) lookup table
///        * Calculate all phi(x, a) = 1 upfront
///        * Stop recursion at c instead of 1
///
///       [1] Tomás Oliveira e Silva, Computing pi(x): the combinatorial
///           method, Revista do DETUA, vol. 4, no. 6, March 2006, p. 761.
///           http://sweet.ua.pt/tos/bib/5.4.pdf
///       [2] phi(x, a) = (x / pp) * φ(pp) + phi(x % pp, a)
///           with pp = 2 * 3 * ... * prime[a]
///
/// Copyright (C) 2016 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <PiTable.hpp>
#include <primecount-internal.hpp>
#include <primecount.hpp>
#include <pmath.hpp>
#include <PhiTiny.hpp>
#include <min_max.hpp>

#include <stdint.h>
#include <algorithm>
#include <vector>
#include <limits>

#ifdef _OPENMP
  #include <omp.h>
#endif

using namespace std;
using namespace primecount;

namespace {

enum
{
  /// Cache phi(x, a) results if a <= MAX_A
  MAX_A = 500,
  /// Keep the cache size below MAX_BYTES per thread
  MAX_BYTES = 16 << 20
};

class PhiCache
{
public:
  PhiCache(vector<int64_t>& primes,
           PiTable& pi) :
    primes_(primes),
    pi_(pi),
    bytes_(0)
  {
    size_t max_size = MAX_A + 1;
    size_t size = min(primes.size(), max_size);
    cache_.resize(size);
  }

  /// Calculate phi(x, a) using the recursive formula:
  /// phi(x, a) = phi(x, a - 1) - phi(x / primes_[a], a - 1)
  ///
  template <int SIGN>
  int64_t phi(int64_t x, int64_t a)
  {
    int64_t sum = 0;

    if (x <= primes_[a])
      sum = SIGN;
    else if (is_phi_tiny(a))
      sum = phi_tiny(x, a) * SIGN;
    else if (is_phi_by_pix(x, a))
      sum = (pi_[x] - a + 1) * SIGN;
    else
    {
      int64_t sqrtx = isqrt(x);
      int64_t pi_sqrtx = a;

      if (sqrtx < pi_.size() && sqrtx < primes_[a])
        pi_sqrtx = pi_[sqrtx];

      // Move out of the loop the calculations where phi(x2, a2) = 1
      // phi(x, a) = 1 if primes_[a] >= x
      // x2 = x / primes_[a2 + 1]
      // phi(x2, a2) = 1 if primes_[a2] >= x / primes_[a2 + 1]
      // phi(x2, a2) = 1 if primes_[a2] >= sqrt(x)
      // phi(x2, a2) = 1 if a2 >= pi(sqrt(x))
      // \sum_{a2 = pi(sqrt(x))}^{a - 1} phi(x2, a2) = a - pi(sqrt(x))
      //
      sum = (a - pi_sqrtx) * -SIGN;

      // phi(x, c) = phi(x, 1) - \sum_{a2 = 1}^{c - 1} phi(x / primes_[a2 + 1], a2)
      int64_t c = min(PhiTiny::max_a(), pi_sqrtx);
      sum += phi_tiny(x, c) * SIGN;
      int64_t a2 = c;

      // 64-bit integer division, slow
      for (; a2 < pi_sqrtx &&
             x > numeric_limits<uint32_t>::max(); a2++)
      {
        int64_t x2 = x / primes_[a2 + 1];
        if (is_cached(x2, a2))
          sum += cache_[a2][x2] * -SIGN;
        else
          sum += phi<-SIGN>(x2, a2);
      }

      // 32-bit integer division, fast
      for (; a2 < pi_sqrtx; a2++)
      {
        int64_t x2 = (uint32_t) x / (uint32_t) primes_[a2 + 1];
        if (is_cached(x2, a2))
          sum += cache_[a2][x2] * -SIGN;
        else
          sum += phi<-SIGN>(x2, a2);
      }
    }

    if (write_to_cache(x, a))
      cache_[a][x] = (uint16_t) (sum * SIGN);

    return sum;
  }

private:
  vector<vector<uint16_t> > cache_;
  vector<int64_t>& primes_;
  PiTable& pi_;
  int64_t bytes_;

  int64_t cache_size(int64_t a) const
  {
    return (int64_t) cache_[a].size();
  }

  bool is_phi_by_pix(int64_t x, int64_t a) const
  {
    return x < pi_.size() &&
           x < isquare(primes_[a + 1]);
  }

  bool is_cached(int64_t x, int64_t a) const
  {
    return a <= MAX_A &&
           x < cache_size(a) &&
           cache_[a][x] != 0;
  }

  bool write_to_cache(int64_t x, int64_t a)
  {
    if (a > MAX_A ||
        x > numeric_limits<uint16_t>::max())
      return false;

    // we need to increase cache size
    if (x >= cache_size(a))
    {
      if (bytes_ > MAX_BYTES)
        return false;
      bytes_ += (x + 1 - cache_size(a)) * 2;
      cache_[a].resize(x + 1, 0);
    }

    return true;
  }
};

} // namespace

namespace primecount {

/// Returns a vector with phi(x, i) values for 0 <= i <= a.
/// phi(x, a) counts the numbers <= x that are not divisible by any
/// of the first a primes.
///
vector<int64_t> phi_vector(int64_t x,
                           int64_t a,
                           vector<int64_t>& primes,
                           PiTable& pi,
                           int threads)
{
  vector<int64_t> phi(a + 2, 0);
  phi[1] = x;

  if (x > 0 && a > 0)
  {
    PhiCache cache(primes, pi);

    int64_t p14 = ipow((int64_t) 10, 14);
    int64_t thread_threshold = p14 / primes[a];
    threads = ideal_num_threads(threads, x, thread_threshold);

    // this loop scales only up to about 8 CPU cores
    // because the cache requires too much memory bandwidth
    threads = min(8, threads);

    #pragma omp parallel for num_threads(threads) schedule(dynamic, 16) firstprivate(cache)
    for (int64_t a2 = 2; a2 <= a; a2++)
      phi[a2] = cache.phi<-1>(x / primes[a2 - 1], a2 - 2);

    // calculate phi(x, i) using partial results
    for (int64_t i = 2; i <= a; i++)
      phi[i] += phi[i - 1];
  }

  return phi;
}

} // namespace
<commit_msg>Refactor<commit_after>///
/// @file  phi_mpi.cpp
/// @brief The PhiCache class calculates the partial sieve function
///        (a.k.a. Legendre-sum) using the recursive formula:
///        phi(x, a) = phi(x, a - 1) - phi(x / primes[a], a - 1).
///        phi(x, a) counts the numbers <= x that are not divisible by
///        any of the first a primes. The algorithm used is an
///        optimized version of the algorithm described in Tomás
///        Oliveira e Silva's paper [1]. I have added 5 optimizations
///        to my implementation which significantly speed up the
///        calculation:
///
///        * Cache results of phi(x, a)
///        * Calculate phi(x, a) using formula [2] if a <= 6
///        * Calculate phi(x, a) using pi(x) lookup table
///        * Calculate all phi(x, a) = 1 upfront
///        * Stop recursion at c instead of 1
///
///       [1] Tomás Oliveira e Silva, Computing pi(x): the combinatorial
///           method, Revista do DETUA, vol. 4, no. 6, March 2006, p. 761.
///           http://sweet.ua.pt/tos/bib/5.4.pdf
///       [2] phi(x, a) = (x / pp) * φ(pp) + phi(x % pp, a)
///           with pp = 2 * 3 * ... * prime[a]
///
/// Copyright (C) 2016 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <PiTable.hpp>
#include <primecount-internal.hpp>
#include <primecount.hpp>
#include <pmath.hpp>
#include <PhiTiny.hpp>
#include <min_max.hpp>

#include <stdint.h>
#include <algorithm>
#include <vector>
#include <limits>

#ifdef _OPENMP
  #include <omp.h>
#endif

using namespace std;
using namespace primecount;

namespace {

/// Cache phi(x, a) results if a <= MAX_A
const int MAX_A = 500;

/// Keep the cache size below MAX_BYTES per thread
const int MAX_BYTES = 16 << 20;

class PhiCache
{
public:
  PhiCache(vector<int64_t>& primes,
           PiTable& pi) :
    primes_(primes),
    pi_(pi),
    bytes_(0)
  {
    size_t max_size = MAX_A + 1;
    size_t size = min(primes.size(), max_size);
    cache_.resize(size);
  }

  /// Calculate phi(x, a) using the recursive formula:
  /// phi(x, a) = phi(x, a - 1) - phi(x / primes_[a], a - 1)
  ///
  template <int SIGN>
  int64_t phi(int64_t x, int64_t a)
  {
    int64_t sum = 0;

    if (x <= primes_[a])
      sum = SIGN;
    else if (is_phi_tiny(a))
      sum = phi_tiny(x, a) * SIGN;
    else if (is_phi_by_pix(x, a))
      sum = (pi_[x] - a + 1) * SIGN;
    else
    {
      int64_t sqrtx = isqrt(x);
      int64_t pi_sqrtx = a;

      if (sqrtx < pi_.size() && sqrtx < primes_[a])
        pi_sqrtx = pi_[sqrtx];

      // Move out of the loop the calculations where phi(x2, a2) = 1
      // phi(x, a) = 1 if primes_[a] >= x
      // x2 = x / primes_[a2 + 1]
      // phi(x2, a2) = 1 if primes_[a2] >= x / primes_[a2 + 1]
      // phi(x2, a2) = 1 if primes_[a2] >= sqrt(x)
      // phi(x2, a2) = 1 if a2 >= pi(sqrt(x))
      // \sum_{a2 = pi(sqrt(x))}^{a - 1} phi(x2, a2) = a - pi(sqrt(x))
      //
      sum = (a - pi_sqrtx) * -SIGN;

      // phi(x, c) = phi(x, 1) - \sum_{a2 = 1}^{c - 1} phi(x / primes_[a2 + 1], a2)
      int64_t c = min(PhiTiny::max_a(), pi_sqrtx);
      sum += phi_tiny(x, c) * SIGN;
      int64_t a2 = c;

      // 64-bit integer division, slow
      for (; a2 < pi_sqrtx &&
             x > numeric_limits<uint32_t>::max(); a2++)
      {
        int64_t x2 = x / primes_[a2 + 1];
        if (is_cached(x2, a2))
          sum += cache_[a2][x2] * -SIGN;
        else
          sum += phi<-SIGN>(x2, a2);
      }

      // 32-bit integer division, fast
      for (; a2 < pi_sqrtx; a2++)
      {
        int64_t x2 = (uint32_t) x / (uint32_t) primes_[a2 + 1];
        if (is_cached(x2, a2))
          sum += cache_[a2][x2] * -SIGN;
        else
          sum += phi<-SIGN>(x2, a2);
      }
    }

    if (write_to_cache(x, a))
      cache_[a][x] = (uint16_t) (sum * SIGN);

    return sum;
  }

private:
  vector<vector<uint16_t> > cache_;
  vector<int64_t>& primes_;
  PiTable& pi_;
  int64_t bytes_;

  int64_t cache_size(int64_t a) const
  {
    return (int64_t) cache_[a].size();
  }

  bool is_phi_by_pix(int64_t x, int64_t a) const
  {
    return x < pi_.size() &&
           x < isquare(primes_[a + 1]);
  }

  bool is_cached(int64_t x, int64_t a) const
  {
    return a <= MAX_A &&
           x < cache_size(a) &&
           cache_[a][x] != 0;
  }

  bool write_to_cache(int64_t x, int64_t a)
  {
    if (a > MAX_A ||
        x > numeric_limits<uint16_t>::max())
      return false;

    // we need to increase cache size
    if (x >= cache_size(a))
    {
      if (bytes_ > MAX_BYTES)
        return false;
      bytes_ += (x + 1 - cache_size(a)) * 2;
      cache_[a].resize(x + 1, 0);
    }

    return true;
  }
};

} // namespace

namespace primecount {

/// Returns a vector with phi(x, i) values for 0 <= i <= a.
/// phi(x, a) counts the numbers <= x that are not divisible by any
/// of the first a primes.
///
vector<int64_t> phi_vector(int64_t x,
                           int64_t a,
                           vector<int64_t>& primes,
                           PiTable& pi,
                           int threads)
{
  vector<int64_t> phi(a + 2, 0);
  phi[1] = x;

  if (x > 0 && a > 0)
  {
    PhiCache cache(primes, pi);

    int64_t p14 = ipow((int64_t) 10, 14);
    int64_t thread_threshold = p14 / primes[a];
    threads = ideal_num_threads(threads, x, thread_threshold);

    // this loop scales only up to about 8 CPU cores
    // because the cache requires too much memory bandwidth
    threads = min(8, threads);

    #pragma omp parallel for num_threads(threads) schedule(dynamic, 16) firstprivate(cache)
    for (int64_t a2 = 2; a2 <= a; a2++)
      phi[a2] = cache.phi<-1>(x / primes[a2 - 1], a2 - 2);

    // calculate phi(x, i) using partial results
    for (int64_t i = 2; i <= a; i++)
      phi[i] += phi[i - 1];
  }

  return phi;
}

} // namespace
<|endoftext|>


<commit_before>/*
 +----------------------------------------------------------------------+
 | Swoole                                                               |
 +----------------------------------------------------------------------+
 | This source file is subject to version 2.0 of the Apache license,    |
 | that is bundled with this package in the file LICENSE, and is        |
 | available through the world-wide-web at the following url:           |
 | http://www.apache.org/licenses/LICENSE-2.0.html                      |
 | If you did not receive a copy of the Apache2.0 license and are unable|
 | to obtain it through the world-wide-web, please send a note to       |
 | license@swoole.com so we can mail you a copy immediately.            |
 +----------------------------------------------------------------------+
 | Author: Tianfeng Han  <mikan.tenny@gmail.com>                        |
 +----------------------------------------------------------------------+
 */

#include "swoole_api.h"
#include "async.h"

#include <thread>
#include <atomic>
#include <unordered_map>
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <queue>

using namespace std;

typedef swAio_event async_event;

struct async_thread_event
{
    /* head */
    enum swAioOpcode type;
    /* info */
    thread::id tid;
};

class async_event_queue
{
public:
    inline void push(async_event *event)
    {
        unique_lock<mutex> lock(_mutex);
        _queue.push(event);
    }

    inline async_event* pop()
    {
        unique_lock<mutex> lock(_mutex);
        if (_queue.empty())
        {
            return nullptr;
        }
        async_event* retval = _queue.front();
        _queue.pop();
        return retval;
    }

    inline double get_max_wait_time()
    {
        unique_lock<mutex> lock(_mutex);
        if (_queue.empty())
        {
            return 0;
        }
        else
        {
            async_event* event = _queue.front();
            return swoole_microtime() - event->timestamp;
        }
    }

    inline size_t count()
    {
        return _queue.size();
    }

private:
    queue<async_event *> _queue;
    mutex _mutex;
};

class async_thread_pool
{
public:
    async_thread_pool(size_t _core_worker_num, size_t _worker_num, double _max_wait_time, double _max_idle_time)
    {
        running = false;

        core_worker_num = _core_worker_num == 0 ? SW_CPU_NUM : SW_MAX(1, _core_worker_num);
        worker_num = _worker_num == 0 ? SW_CPU_NUM * SW_AIO_THREAD_NUM_MULTIPLE : SW_MAX(core_worker_num, _worker_num);
        max_wait_time = _max_wait_time == 0 ? SW_AIO_TASK_MAX_WAIT_TIME : _max_wait_time;
        max_idle_time = _max_idle_time == 0 ? SW_AIO_THREAD_MAX_IDLE_TIME : _max_idle_time;

        current_pid = getpid();

        if (swPipeBase_create(&_aio_pipe, 0) < 0)
        {
            swoole_throw_error(SW_ERROR_SYSTEM_CALL_FAIL);
        }
        _pipe_read = _aio_pipe.getFd(&_aio_pipe, 0);
        _pipe_write = _aio_pipe.getFd(&_aio_pipe, 1);
        swoole_event_add(_pipe_read, SW_EVENT_READ, SW_FD_AIO);
    }

    ~async_thread_pool()
    {
        shutdown();
        if (SwooleTG.reactor)
        {
            swoole_event_del(_pipe_read);
        }
        _aio_pipe.close(&_aio_pipe);
    }

    bool start()
    {
        running = true;
        current_task_id = 0;
        n_waiting = 0;
        n_closing = 0;
        for (size_t i = 0; i < core_worker_num; i++)
        {
            create_thread(true);
        }
        return true;
    }

    bool shutdown()
    {
        if (!running)
        {
            return false;
        }
        running = false;

        event_mutex.lock();
        _cv.notify_all();
        event_mutex.unlock();

        for (auto &i : threads)
        {
            thread *_thread = i.second;
            if (_thread->joinable())
            {
                _thread->join();
            }
            delete _thread;
        }

        return true;
    }

    void schedule()
    {
        if (n_waiting == 0 && threads.size() < worker_num && max_wait_time > 0) {
            double _max_wait_time = _queue.get_max_wait_time();
            if (_max_wait_time > max_wait_time)
            {
                size_t n = 1; /* maybe we can find a better strategy */
                if (threads.size() + n > worker_num)
                {
                    n = worker_num - threads.size();
                }
                swTraceLog(SW_TRACE_AIO, "Create %zu thread due to wait %fs, we will have %zu threads", n, _max_wait_time, threads.size() + n);
                while (n--)
                {
                    create_thread();
                }
            }
        }
    }

    async_event* dispatch(const async_event *request)
    {
        schedule();

        auto _event_copy = new async_event(*request);
        _event_copy->task_id = current_task_id++;
        _event_copy->timestamp = swoole_microtime();
        _queue.push(_event_copy);
        _cv.notify_one();
        return _event_copy;
    }

    inline size_t worker_count()
    {
        return threads.size();
    }

    inline size_t queue_count()
    {
        return _queue.count();
    }

    pid_t current_pid;

    void release_thread(thread::id tid)
    {
        auto i = threads.find(tid);
        if (i == threads.end())
        {
            swWarn("AIO thread#%zu is missing", tid);
            return;
        }
        else
        {
            thread *_thread = i->second;
            swTraceLog(SW_TRACE_AIO, "release idle thread#%zu, we have %zu now", tid, threads.size() - 1);
            if (_thread->joinable()) {
                _thread->join();
            }
            threads.erase(i);
            delete _thread;
        }
    }

private:

    void create_thread(const bool is_core_worker = false)
    {
        try
        {
            thread *_thread = new thread([this, is_core_worker]()
            {
                bool exit_flag = false;

                SwooleTG.buffer_stack = swString_new(SW_STACK_BUFFER_SIZE);
                if (SwooleTG.buffer_stack == nullptr)
                {
                    return;
                }

                swSignal_none();

                while (running)
                {
                    async_event *event = _queue.pop();
                    if (event)
                    {
                        if (sw_unlikely(event->handler == nullptr))
                        {
                            event->error = SW_ERROR_AIO_BAD_REQUEST;
                            event->ret = -1;
                        }
                        else if (sw_unlikely(event->canceled))
                        {
                            event->error = SW_ERROR_AIO_CANCELED;
                            event->ret = -1;
                        }
                        else
                        {
                            event->handler(event);
                        }

                        // swTraceLog(SW_TRACE_AIO, "aio_thread %s. ret=%d, error=%d", event->ret > 0 ? "ok" : "failed", event->ret, event->error);

                        _send_event:
                        while (true)
                        {
                            SwooleAIO.lock.lock(&SwooleAIO.lock);
                            int ret = write(_pipe_write, &event, sizeof(event));
                            SwooleAIO.lock.unlock(&SwooleAIO.lock);
                            if (ret < 0)
                            {
                                if (errno == EAGAIN)
                                {
                                    swSocket_wait(_pipe_write, 1000, SW_EVENT_WRITE);
                                    continue;
                                }
                                else if (errno == EINTR)
                                {
                                    continue;
                                }
                                else
                                {
                                    swSysWarn("sendto swoole_aio_pipe_write failed");
                                }
                            }
                            break;
                        }

                        // exit
                        if (exit_flag)
                        {
                            n_closing--;
                            break;
                        }
                    }
                    else if (running)
                    {
                        unique_lock<mutex> lock(event_mutex);
                        ++n_waiting;
                        if (is_core_worker || max_idle_time <= 0)
                        {
                            _cv.wait(lock);
                        }
                        else
                        {
                            while (true)
                            {
                                if (_cv.wait_for(lock, chrono::microseconds((size_t) (max_idle_time * 1000 * 1000))) == cv_status::timeout)
                                {
                                    if (running && n_closing != 0)
                                    {
                                        // wait for the next round
                                        continue;
                                    }
                                    /* notifies the main thread to release this thread */
                                    async_thread_event *_event = new async_thread_event;
                                    _event->type = SW_AIO_RELEASE_THREAD;
                                    _event->tid = this_thread::get_id();
                                    event = (async_event *) _event;
                                    --n_waiting;
                                    ++n_closing;
                                    exit_flag = true;
                                    goto _send_event;
                                }
                            }
                        }
                        --n_waiting;
                    }
                }
            });
            threads[_thread->get_id()] = _thread;
        }
        catch (const std::system_error& e)
        {
            swSysNotice("create aio thread failed, please check your system configuration or adjust aio_worker_num");
            return;
        }
    }

    size_t core_worker_num;
    size_t worker_num;
    double max_wait_time;
    double max_idle_time;

    swPipe _aio_pipe;
    int _pipe_read;
    int _pipe_write;

    bool running;

    atomic<size_t> n_waiting;
    atomic<size_t> n_closing;
    size_t current_task_id = 0;

    unordered_map<thread::id, thread *> threads;
    async_event_queue _queue;
    mutex event_mutex;
    condition_variable _cv;
};

static async_thread_pool *pool = nullptr;

swAsyncIO SwooleAIO;

static void swAio_free(void *private_data)
{
    if (!SwooleAIO.init)
    {
        return;
    }
    if (pool->current_pid == getpid())
    {
        delete pool;
    }
    pool = nullptr;
    SwooleAIO.init = 0;
}

static int swAio_init()
{
    if (SwooleAIO.init)
    {
        swWarn("AIO has already been initialized");
        return SW_ERR;
    }
    if (!SwooleTG.reactor)
    {
        swWarn("no event loop, cannot initialized");
        return SW_ERR;
    }

    if (swMutex_create(&SwooleAIO.lock, 0) < 0)
    {
        swWarn("create mutex lock error");
        return SW_ERR;
    }

    swReactor_add_destroy_callback(SwooleTG.reactor, swAio_free, nullptr);

    pool = new async_thread_pool(SwooleAIO.min_thread_num, SwooleAIO.max_thread_num, SwooleAIO.max_wait_time, SwooleAIO.max_idle_time);
    pool->start();
    SwooleAIO.init = 1;

    return SW_OK;
}

size_t swAio_thread_count()
{
    return pool ? pool->worker_count() : 0;
}

ssize_t swAio_dispatch(const swAio_event *request)
{
    if (sw_unlikely(!SwooleAIO.init))
    {
        swAio_init();
    }
    SwooleAIO.task_num++;
    async_event *event = pool->dispatch(request);
    return event->task_id;
}

swAio_event* swAio_dispatch2(const swAio_event *request)
{
    if (sw_unlikely(!SwooleAIO.init))
    {
        swAio_init();
    }
    SwooleAIO.task_num++;
    return pool->dispatch(request);
}

int swAio_callback(swReactor *reactor, swEvent *event)
{
    pool->schedule();

    async_event *events[SW_AIO_EVENT_NUM];
    ssize_t n = read(event->fd, events, sizeof(async_event *) * SW_AIO_EVENT_NUM);
    if (n < 0)
    {
        swSysWarn("read() aio events failed");
        return SW_ERR;
    }
    for (size_t i = 0; i < n / sizeof(async_event *); i++)
    {
        async_event *event = events[i];
        if (event->type == SW_AIO_RELEASE_THREAD)
        {
            async_thread_event *event = (async_thread_event *) events[i];
            pool->release_thread(event->tid);
            delete event;
        }
        else
        {
            if (!event->canceled)
            {
                event->callback(events[i]);
            }
            SwooleAIO.task_num--;
            delete event;
        }
    }

    return SW_OK;
}
<commit_msg>Fix missing break statement<commit_after>/*
 +----------------------------------------------------------------------+
 | Swoole                                                               |
 +----------------------------------------------------------------------+
 | This source file is subject to version 2.0 of the Apache license,    |
 | that is bundled with this package in the file LICENSE, and is        |
 | available through the world-wide-web at the following url:           |
 | http://www.apache.org/licenses/LICENSE-2.0.html                      |
 | If you did not receive a copy of the Apache2.0 license and are unable|
 | to obtain it through the world-wide-web, please send a note to       |
 | license@swoole.com so we can mail you a copy immediately.            |
 +----------------------------------------------------------------------+
 | Author: Tianfeng Han  <mikan.tenny@gmail.com>                        |
 +----------------------------------------------------------------------+
 */

#include "swoole_api.h"
#include "async.h"

#include <thread>
#include <atomic>
#include <unordered_map>
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <queue>

using namespace std;

typedef swAio_event async_event;

struct async_thread_event
{
    /* head */
    enum swAioOpcode type;
    /* info */
    thread::id tid;
};

class async_event_queue
{
public:
    inline void push(async_event *event)
    {
        unique_lock<mutex> lock(_mutex);
        _queue.push(event);
    }

    inline async_event* pop()
    {
        unique_lock<mutex> lock(_mutex);
        if (_queue.empty())
        {
            return nullptr;
        }
        async_event* retval = _queue.front();
        _queue.pop();
        return retval;
    }

    inline double get_max_wait_time()
    {
        unique_lock<mutex> lock(_mutex);
        if (_queue.empty())
        {
            return 0;
        }
        else
        {
            async_event* event = _queue.front();
            return swoole_microtime() - event->timestamp;
        }
    }

    inline size_t count()
    {
        return _queue.size();
    }

private:
    queue<async_event *> _queue;
    mutex _mutex;
};

class async_thread_pool
{
public:
    async_thread_pool(size_t _core_worker_num, size_t _worker_num, double _max_wait_time, double _max_idle_time)
    {
        running = false;

        core_worker_num = _core_worker_num == 0 ? SW_CPU_NUM : SW_MAX(1, _core_worker_num);
        worker_num = _worker_num == 0 ? SW_CPU_NUM * SW_AIO_THREAD_NUM_MULTIPLE : SW_MAX(core_worker_num, _worker_num);
        max_wait_time = _max_wait_time == 0 ? SW_AIO_TASK_MAX_WAIT_TIME : _max_wait_time;
        max_idle_time = _max_idle_time == 0 ? SW_AIO_THREAD_MAX_IDLE_TIME : _max_idle_time;

        current_pid = getpid();

        if (swPipeBase_create(&_aio_pipe, 0) < 0)
        {
            swoole_throw_error(SW_ERROR_SYSTEM_CALL_FAIL);
        }
        _pipe_read = _aio_pipe.getFd(&_aio_pipe, 0);
        _pipe_write = _aio_pipe.getFd(&_aio_pipe, 1);
        swoole_event_add(_pipe_read, SW_EVENT_READ, SW_FD_AIO);
    }

    ~async_thread_pool()
    {
        shutdown();
        if (SwooleTG.reactor)
        {
            swoole_event_del(_pipe_read);
        }
        _aio_pipe.close(&_aio_pipe);
    }

    bool start()
    {
        running = true;
        current_task_id = 0;
        n_waiting = 0;
        n_closing = 0;
        for (size_t i = 0; i < core_worker_num; i++)
        {
            create_thread(true);
        }
        return true;
    }

    bool shutdown()
    {
        if (!running)
        {
            return false;
        }
        running = false;

        event_mutex.lock();
        _cv.notify_all();
        event_mutex.unlock();

        for (auto &i : threads)
        {
            thread *_thread = i.second;
            if (_thread->joinable())
            {
                _thread->join();
            }
            delete _thread;
        }

        return true;
    }

    void schedule()
    {
        if (n_waiting == 0 && threads.size() < worker_num && max_wait_time > 0) {
            double _max_wait_time = _queue.get_max_wait_time();
            if (_max_wait_time > max_wait_time)
            {
                size_t n = 1; /* maybe we can find a better strategy */
                if (threads.size() + n > worker_num)
                {
                    n = worker_num - threads.size();
                }
                swTraceLog(SW_TRACE_AIO, "Create %zu thread due to wait %fs, we will have %zu threads", n, _max_wait_time, threads.size() + n);
                while (n--)
                {
                    create_thread();
                }
            }
        }
    }

    async_event* dispatch(const async_event *request)
    {
        schedule();

        auto _event_copy = new async_event(*request);
        _event_copy->task_id = current_task_id++;
        _event_copy->timestamp = swoole_microtime();
        _queue.push(_event_copy);
        _cv.notify_one();
        return _event_copy;
    }

    inline size_t worker_count()
    {
        return threads.size();
    }

    inline size_t queue_count()
    {
        return _queue.count();
    }

    pid_t current_pid;

    void release_thread(thread::id tid)
    {
        auto i = threads.find(tid);
        if (i == threads.end())
        {
            swWarn("AIO thread#%zu is missing", tid);
            return;
        }
        else
        {
            thread *_thread = i->second;
            swTraceLog(SW_TRACE_AIO, "release idle thread#%zu, we have %zu now", tid, threads.size() - 1);
            if (_thread->joinable()) {
                _thread->join();
            }
            threads.erase(i);
            delete _thread;
        }
    }

private:

    void create_thread(const bool is_core_worker = false)
    {
        try
        {
            thread *_thread = new thread([this, is_core_worker]()
            {
                bool exit_flag = false;

                SwooleTG.buffer_stack = swString_new(SW_STACK_BUFFER_SIZE);
                if (SwooleTG.buffer_stack == nullptr)
                {
                    return;
                }

                swSignal_none();

                while (running)
                {
                    async_event *event = _queue.pop();
                    if (event)
                    {
                        if (sw_unlikely(event->handler == nullptr))
                        {
                            event->error = SW_ERROR_AIO_BAD_REQUEST;
                            event->ret = -1;
                        }
                        else if (sw_unlikely(event->canceled))
                        {
                            event->error = SW_ERROR_AIO_CANCELED;
                            event->ret = -1;
                        }
                        else
                        {
                            event->handler(event);
                        }

                        // swTraceLog(SW_TRACE_AIO, "aio_thread %s. ret=%d, error=%d", event->ret > 0 ? "ok" : "failed", event->ret, event->error);

                        _send_event:
                        while (true)
                        {
                            SwooleAIO.lock.lock(&SwooleAIO.lock);
                            int ret = write(_pipe_write, &event, sizeof(event));
                            SwooleAIO.lock.unlock(&SwooleAIO.lock);
                            if (ret < 0)
                            {
                                if (errno == EAGAIN)
                                {
                                    swSocket_wait(_pipe_write, 1000, SW_EVENT_WRITE);
                                    continue;
                                }
                                else if (errno == EINTR)
                                {
                                    continue;
                                }
                                else
                                {
                                    swSysWarn("sendto swoole_aio_pipe_write failed");
                                }
                            }
                            break;
                        }

                        // exit
                        if (exit_flag)
                        {
                            n_closing--;
                            break;
                        }
                    }
                    else if (running)
                    {
                        unique_lock<mutex> lock(event_mutex);
                        ++n_waiting;
                        if (is_core_worker || max_idle_time <= 0)
                        {
                            _cv.wait(lock);
                        }
                        else
                        {
                            while (true)
                            {
                                if (_cv.wait_for(lock, chrono::microseconds((size_t) (max_idle_time * 1000 * 1000))) == cv_status::timeout)
                                {
                                    if (running && n_closing != 0)
                                    {
                                        // wait for the next round
                                        continue;
                                    }
                                    /* notifies the main thread to release this thread */
                                    async_thread_event *_event = new async_thread_event;
                                    _event->type = SW_AIO_RELEASE_THREAD;
                                    _event->tid = this_thread::get_id();
                                    event = (async_event *) _event;
                                    --n_waiting;
                                    ++n_closing;
                                    exit_flag = true;
                                    goto _send_event;
                                }
                                break;
                            }
                        }
                        --n_waiting;
                    }
                }
            });
            threads[_thread->get_id()] = _thread;
        }
        catch (const std::system_error& e)
        {
            swSysNotice("create aio thread failed, please check your system configuration or adjust aio_worker_num");
            return;
        }
    }

    size_t core_worker_num;
    size_t worker_num;
    double max_wait_time;
    double max_idle_time;

    swPipe _aio_pipe;
    int _pipe_read;
    int _pipe_write;

    bool running;

    atomic<size_t> n_waiting;
    atomic<size_t> n_closing;
    size_t current_task_id = 0;

    unordered_map<thread::id, thread *> threads;
    async_event_queue _queue;
    mutex event_mutex;
    condition_variable _cv;
};

static async_thread_pool *pool = nullptr;

swAsyncIO SwooleAIO;

static void swAio_free(void *private_data)
{
    if (!SwooleAIO.init)
    {
        return;
    }
    if (pool->current_pid == getpid())
    {
        delete pool;
    }
    pool = nullptr;
    SwooleAIO.init = 0;
}

static int swAio_init()
{
    if (SwooleAIO.init)
    {
        swWarn("AIO has already been initialized");
        return SW_ERR;
    }
    if (!SwooleTG.reactor)
    {
        swWarn("no event loop, cannot initialized");
        return SW_ERR;
    }

    if (swMutex_create(&SwooleAIO.lock, 0) < 0)
    {
        swWarn("create mutex lock error");
        return SW_ERR;
    }

    swReactor_add_destroy_callback(SwooleTG.reactor, swAio_free, nullptr);

    pool = new async_thread_pool(SwooleAIO.min_thread_num, SwooleAIO.max_thread_num, SwooleAIO.max_wait_time, SwooleAIO.max_idle_time);
    pool->start();
    SwooleAIO.init = 1;

    return SW_OK;
}

size_t swAio_thread_count()
{
    return pool ? pool->worker_count() : 0;
}

ssize_t swAio_dispatch(const swAio_event *request)
{
    if (sw_unlikely(!SwooleAIO.init))
    {
        swAio_init();
    }
    SwooleAIO.task_num++;
    async_event *event = pool->dispatch(request);
    return event->task_id;
}

swAio_event* swAio_dispatch2(const swAio_event *request)
{
    if (sw_unlikely(!SwooleAIO.init))
    {
        swAio_init();
    }
    SwooleAIO.task_num++;
    return pool->dispatch(request);
}

int swAio_callback(swReactor *reactor, swEvent *event)
{
    pool->schedule();

    async_event *events[SW_AIO_EVENT_NUM];
    ssize_t n = read(event->fd, events, sizeof(async_event *) * SW_AIO_EVENT_NUM);
    if (n < 0)
    {
        swSysWarn("read() aio events failed");
        return SW_ERR;
    }
    for (size_t i = 0; i < n / sizeof(async_event *); i++)
    {
        async_event *event = events[i];
        if (event->type == SW_AIO_RELEASE_THREAD)
        {
            async_thread_event *event = (async_thread_event *) events[i];
            pool->release_thread(event->tid);
            delete event;
        }
        else
        {
            if (!event->canceled)
            {
                event->callback(events[i]);
            }
            SwooleAIO.task_num--;
            delete event;
        }
    }

    return SW_OK;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2015 - 2017 gtalent2@gmail.com
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ox/std/strops.hpp>
#include <ox/fs/filesystem.hpp>

// suppress warnings about using fopen
#ifdef _MSC_VER
#pragma warning(disable:4996)
#endif

using namespace ox::fs;
using namespace std;

const static auto oxfstoolVersion = "1.1.1";
const static auto usage = "usage:\n"
"\toxfs format [16,32,64] <size> <path>\n"
"\toxfs read <FS file> <inode>\n"
"\toxfs write <FS file> <inode> <insertion file>\n"
"\toxfs write-expand <FS file> <inode> <insertion file>\n"
"\toxfs rm <FS file> <inode>\n"
"\toxfs compact <FS file>\n"
"\toxfs version\n";

char *loadFileBuff(const char *path, ::size_t *sizeOut = nullptr) {
	auto file = fopen(path, "rb");
	if (file) {
		fseek(file, 0, SEEK_END);
		const auto size = ftell(file);
		rewind(file);
		auto buff = (char*) malloc(size);
		auto itemsRead = fread(buff, size, 1, file);
		fclose(file);
		if (sizeOut) {
			*sizeOut = itemsRead ? size : 0;
		}
		return buff;
	} else {
		return nullptr;
	}
}

size_t bytes(const char *str) {
	auto size = ::ox_strlen(str);
	const auto lastChar = str[size-1];
	auto multiplier = 1;
	auto copy = new char[size];
	ox_memcpy(copy, str, size);
	if (lastChar < '0' || lastChar > '9') {
		copy[size-1] = 0;
		switch (lastChar) {
			case 'k':
			case 'K':
				multiplier = 1024;
				break;
			case 'm':
			case 'M':
				multiplier = 1024 * 1024;
				break;
			case 'g':
			case 'G':
				multiplier = 1024 * 1024 * 1024;
				break;
			default:
				multiplier = -1;
		}
	}
	const auto retval = ((size_t) ox_atoi(copy)) * multiplier;
	delete []copy;
	return  retval;
}

int format(int argc, char **args) {
	printf("Creating file system...\n");
	auto err = 0;
	if (argc >= 5) {
		auto type = ox_atoi(args[2]);
		cout << args[3] << endl;
		auto size = bytes(args[3]);
		auto path = args[4];
		auto buff = (uint8_t*) malloc(size);

		cout << "Size: " << size << " bytes\n";
		cout << "Type: " << type << endl;

		if (size < sizeof(FileStore64)) {
			err = 1;
			cerr <<  "File system size " << size << " too small, must be at least " << sizeof(FileStore64) << endl;
		}

		if (!err) {
			// format
			switch (type) {
				case 16:
					FileStore16::format(buff, (FileStore16::FsSize_t) size, ox::fs::OxFS_16);
					break;
				case 32:
					FileStore32::format(buff, (FileStore32::FsSize_t) size, ox::fs::OxFS_32);
					break;
				case 64:
					FileStore64::format(buff, size, ox::fs::OxFS_64);
					break;
				default:
					err = 1;
			}

			if (!err) {
				auto file = fopen(path, "wb");
				if (file) {
					err = fwrite(buff, size, 1, file) != 1;
					err |= fclose(file);
					if (err) {
						fprintf(stderr, "Could not write to file: %s.\n", path);
					}
				} else {
					fprintf(stderr, "Could not open file: %s.\n", path);
				}
			}
		}

		free(buff);

		if (err == 0) {
			fprintf(stderr, "Created file system %s\n", path);
		}
	} else {
		fprintf(stderr, "Insufficient arguments\n");
	}

	return err;
}

int read(int argc, char **args) {
	auto err = 1;
	if (argc >= 4) {
		auto fsPath = args[2];
		auto inode = ox_atoi(args[3]);
		size_t fsSize;
		size_t fileSize;

		auto fsBuff = loadFileBuff(fsPath, &fsSize);

		if (fsBuff) {
			auto fs = createFileSystem(fsBuff);

			if (fs) {
				auto output = fs->read(inode, &fileSize);

				if (output) {
					fwrite(output, fileSize, 1, stdout);
					delete []output;
					err = 0;
				}

				delete fs;
				free(fsBuff);
			} else {
				fprintf(stderr, "Invalid file system type: %d.\n", *(uint32_t*) fsBuff);
			}
		} else {
			fprintf(stderr, "Could not open file: %s\n", fsPath);
		}
	} else {
		fprintf(stderr, "Insufficient arguments\n");
	}
	return err;
}

int write(int argc, char **args, bool expand) {
	auto err = 0;
	if (argc >= 5) {
		auto fsPath = args[2];
		auto inode = ox_atoi(args[3]);
		auto srcPath = args[4];
		size_t srcSize;

		auto fsFile = fopen(fsPath, "rb");
		if (fsFile) {
			fseek(fsFile, 0, SEEK_END);

			auto fsSize = (size_t) ftell(fsFile);
			rewind(fsFile);
			auto fsBuff = new uint8_t[fsSize];
			auto itemsRead = fread(fsBuff, fsSize, 1, fsFile);
			fclose(fsFile);

			if (itemsRead) {
				auto srcBuff = loadFileBuff(srcPath, &srcSize);
				if (srcBuff) {
					auto fs = createFileSystem(fsBuff);
					if (fs) {
						if (expand && fs->available() <= srcSize) {
							auto needed = fs->spaceNeeded(inode, srcSize);
							auto cloneBuff = new uint8_t[needed];
							ox_memcpy(cloneBuff, fsBuff, fsSize);

							delete fs;
							delete []fsBuff;

							fsBuff = cloneBuff;
							fs = createFileSystem(fsBuff);
							fsSize = needed;
							fs->resize(fsSize);
						}
						err |= fs->write(inode, srcBuff, srcSize);
						if (err) {
							fprintf(stderr, "Could not write to file system.\n");
						}
						delete fs;
					} else {
						fprintf(stderr, "Invalid file system type: %d.\n", *(uint32_t*) fsBuff);
						err = 1;
					}

					if (!err) {
						fsFile = fopen(fsPath, "wb");

						if (fsFile) {
							err = fwrite(fsBuff, fsSize, 1, fsFile) != 1;
							err |= fclose(fsFile);
							if (err) {
								fprintf(stderr, "Could not write to file system file.\n");
							}
						} else {
							err = 1;
						}
					}
					free(srcBuff);
				} else {
					err = 1;
					fprintf(stderr, "Could not load source file: %s.\n", srcPath);
				}
			}

			delete []fsBuff;
		} else {
			fprintf(stderr, "Could not open file system\n");
		}
	} else {
		fprintf(stderr, "Insufficient arguments\n");
	}
	return err;
}

int compact(int argc, char **args) {
	auto err = 1;
	if (argc >= 2) {
		auto fsPath = args[2];
		size_t fsSize;

		auto fsBuff = loadFileBuff(fsPath, &fsSize);
		if (fsBuff) {
			auto fs = createFileSystem(fsBuff);

			if (fs) {
				fs->resize();
			} else {
				fprintf(stderr, "Invalid file system.\n");
			}

			// write back to file
			auto fsFile = fopen(fsPath, "wb");
			if (fsFile) {
				err = fwrite(fsBuff, fs->size(), 1, fsFile) != 1;
				err |= fclose(fsFile);
				if (err) {
					fprintf(stderr, "Could not write to file system file.\n");
				}
			} else {
				err = 1;
			}

			delete fs;
			free(fsBuff);
		} else {
			fprintf(stderr, "Could not open file: %s\n", fsPath);
		}
	} else {
		fprintf(stderr, "Insufficient arguments\n");
	}
	return err;
}

int remove(int argc, char **args) {
	auto err = 1;
	if (argc >= 4) {
		auto fsPath = args[2];
		auto inode = ox_atoi(args[3]);
		size_t fsSize;

		auto fsBuff = loadFileBuff(fsPath, &fsSize);
		if (fsBuff) {
			auto fs = createFileSystem(fsBuff);

			if (fs) {
				err = fs->remove(inode);
			} else {
				fprintf(stderr, "Invalid file system.\n");
			}

			if (err) {
				fprintf(stderr, "Could not write to file system.\n");
			} else {
				auto fsFile = fopen(fsPath, "wb");
				if (fsFile) {
					err = fwrite(fsBuff, fsSize, 1, fsFile) != 1;
					err |= fclose(fsFile);
					if (err) {
						fprintf(stderr, "Could not write to file system file.\n");
					}
				} else {
					err = 1;
				}
			}

			delete fs;
			free(fsBuff);
		} else {
			fprintf(stderr, "Could not open file: %s\n", fsPath);
		}
	} else {
		fprintf(stderr, "Insufficient arguments\n");
	}
	return err;
}

int main(int argc, char **args) {
	auto err = 0;
	if (argc > 1) {
		auto cmd = args[1];
		if (ox_strcmp(cmd, "format") == 0) {
			err = format(argc, args);
		} else if (ox_strcmp(cmd, "read") == 0) {
			err = read(argc, args);
		} else if (ox_strcmp(cmd, "write") == 0) {
			err = write(argc, args, false);
		} else if (ox_strcmp(cmd, "write-expand") == 0) {
			err = write(argc, args, true);
		} else if (ox_strcmp(cmd, "compact") == 0) {
			err = compact(argc, args);
		} else if (ox_strcmp(cmd, "rm") == 0) {
			err = remove(argc, args);
		} else if (ox_strcmp(cmd, "help") == 0) {
			printf("%s\n", usage);
		} else if (ox_strcmp(cmd, "version") == 0) {
			printf("oxfstool version %s\n", oxfstoolVersion);
			printf("oxfs format version %d\n", FileStore16::VERSION);
		} else {
			printf("Command '%s' not recognized.\n", cmd);
			err = 1;
		}
	} else {
		printf("%s\n", usage);
	}
	return err;
}
<commit_msg>Increment the oxfstool version.<commit_after>/*
 * Copyright 2015 - 2017 gtalent2@gmail.com
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ox/std/strops.hpp>
#include <ox/fs/filesystem.hpp>

// suppress warnings about using fopen
#ifdef _MSC_VER
#pragma warning(disable:4996)
#endif

using namespace ox::fs;
using namespace std;

const static auto oxfstoolVersion = "1.2.0";
const static auto usage = "usage:\n"
"\toxfs format [16,32,64] <size> <path>\n"
"\toxfs read <FS file> <inode>\n"
"\toxfs write <FS file> <inode> <insertion file>\n"
"\toxfs write-expand <FS file> <inode> <insertion file>\n"
"\toxfs rm <FS file> <inode>\n"
"\toxfs compact <FS file>\n"
"\toxfs version\n";

char *loadFileBuff(const char *path, ::size_t *sizeOut = nullptr) {
	auto file = fopen(path, "rb");
	if (file) {
		fseek(file, 0, SEEK_END);
		const auto size = ftell(file);
		rewind(file);
		auto buff = (char*) malloc(size);
		auto itemsRead = fread(buff, size, 1, file);
		fclose(file);
		if (sizeOut) {
			*sizeOut = itemsRead ? size : 0;
		}
		return buff;
	} else {
		return nullptr;
	}
}

size_t bytes(const char *str) {
	auto size = ::ox_strlen(str);
	const auto lastChar = str[size-1];
	auto multiplier = 1;
	auto copy = new char[size];
	ox_memcpy(copy, str, size);
	if (lastChar < '0' || lastChar > '9') {
		copy[size-1] = 0;
		switch (lastChar) {
			case 'k':
			case 'K':
				multiplier = 1024;
				break;
			case 'm':
			case 'M':
				multiplier = 1024 * 1024;
				break;
			case 'g':
			case 'G':
				multiplier = 1024 * 1024 * 1024;
				break;
			default:
				multiplier = -1;
		}
	}
	const auto retval = ((size_t) ox_atoi(copy)) * multiplier;
	delete []copy;
	return  retval;
}

int format(int argc, char **args) {
	printf("Creating file system...\n");
	auto err = 0;
	if (argc >= 5) {
		auto type = ox_atoi(args[2]);
		cout << args[3] << endl;
		auto size = bytes(args[3]);
		auto path = args[4];
		auto buff = (uint8_t*) malloc(size);

		cout << "Size: " << size << " bytes\n";
		cout << "Type: " << type << endl;

		if (size < sizeof(FileStore64)) {
			err = 1;
			cerr <<  "File system size " << size << " too small, must be at least " << sizeof(FileStore64) << endl;
		}

		if (!err) {
			// format
			switch (type) {
				case 16:
					FileStore16::format(buff, (FileStore16::FsSize_t) size, ox::fs::OxFS_16);
					break;
				case 32:
					FileStore32::format(buff, (FileStore32::FsSize_t) size, ox::fs::OxFS_32);
					break;
				case 64:
					FileStore64::format(buff, size, ox::fs::OxFS_64);
					break;
				default:
					err = 1;
			}

			if (!err) {
				auto file = fopen(path, "wb");
				if (file) {
					err = fwrite(buff, size, 1, file) != 1;
					err |= fclose(file);
					if (err) {
						fprintf(stderr, "Could not write to file: %s.\n", path);
					}
				} else {
					fprintf(stderr, "Could not open file: %s.\n", path);
				}
			}
		}

		free(buff);

		if (err == 0) {
			fprintf(stderr, "Created file system %s\n", path);
		}
	} else {
		fprintf(stderr, "Insufficient arguments\n");
	}

	return err;
}

int read(int argc, char **args) {
	auto err = 1;
	if (argc >= 4) {
		auto fsPath = args[2];
		auto inode = ox_atoi(args[3]);
		size_t fsSize;
		size_t fileSize;

		auto fsBuff = loadFileBuff(fsPath, &fsSize);

		if (fsBuff) {
			auto fs = createFileSystem(fsBuff);

			if (fs) {
				auto output = fs->read(inode, &fileSize);

				if (output) {
					fwrite(output, fileSize, 1, stdout);
					delete []output;
					err = 0;
				}

				delete fs;
				free(fsBuff);
			} else {
				fprintf(stderr, "Invalid file system type: %d.\n", *(uint32_t*) fsBuff);
			}
		} else {
			fprintf(stderr, "Could not open file: %s\n", fsPath);
		}
	} else {
		fprintf(stderr, "Insufficient arguments\n");
	}
	return err;
}

int write(int argc, char **args, bool expand) {
	auto err = 0;
	if (argc >= 5) {
		auto fsPath = args[2];
		auto inode = ox_atoi(args[3]);
		auto srcPath = args[4];
		size_t srcSize;

		auto fsFile = fopen(fsPath, "rb");
		if (fsFile) {
			fseek(fsFile, 0, SEEK_END);

			auto fsSize = (size_t) ftell(fsFile);
			rewind(fsFile);
			auto fsBuff = new uint8_t[fsSize];
			auto itemsRead = fread(fsBuff, fsSize, 1, fsFile);
			fclose(fsFile);

			if (itemsRead) {
				auto srcBuff = loadFileBuff(srcPath, &srcSize);
				if (srcBuff) {
					auto fs = createFileSystem(fsBuff);
					if (fs) {
						if (expand && fs->available() <= srcSize) {
							auto needed = fs->spaceNeeded(inode, srcSize);
							auto cloneBuff = new uint8_t[needed];
							ox_memcpy(cloneBuff, fsBuff, fsSize);

							delete fs;
							delete []fsBuff;

							fsBuff = cloneBuff;
							fs = createFileSystem(fsBuff);
							fsSize = needed;
							fs->resize(fsSize);
						}
						err |= fs->write(inode, srcBuff, srcSize);
						if (err) {
							fprintf(stderr, "Could not write to file system.\n");
						}
						delete fs;
					} else {
						fprintf(stderr, "Invalid file system type: %d.\n", *(uint32_t*) fsBuff);
						err = 1;
					}

					if (!err) {
						fsFile = fopen(fsPath, "wb");

						if (fsFile) {
							err = fwrite(fsBuff, fsSize, 1, fsFile) != 1;
							err |= fclose(fsFile);
							if (err) {
								fprintf(stderr, "Could not write to file system file.\n");
							}
						} else {
							err = 1;
						}
					}
					free(srcBuff);
				} else {
					err = 1;
					fprintf(stderr, "Could not load source file: %s.\n", srcPath);
				}
			}

			delete []fsBuff;
		} else {
			fprintf(stderr, "Could not open file system\n");
		}
	} else {
		fprintf(stderr, "Insufficient arguments\n");
	}
	return err;
}

int compact(int argc, char **args) {
	auto err = 1;
	if (argc >= 2) {
		auto fsPath = args[2];
		size_t fsSize;

		auto fsBuff = loadFileBuff(fsPath, &fsSize);
		if (fsBuff) {
			auto fs = createFileSystem(fsBuff);

			if (fs) {
				fs->resize();
			} else {
				fprintf(stderr, "Invalid file system.\n");
			}

			// write back to file
			auto fsFile = fopen(fsPath, "wb");
			if (fsFile) {
				err = fwrite(fsBuff, fs->size(), 1, fsFile) != 1;
				err |= fclose(fsFile);
				if (err) {
					fprintf(stderr, "Could not write to file system file.\n");
				}
			} else {
				err = 1;
			}

			delete fs;
			free(fsBuff);
		} else {
			fprintf(stderr, "Could not open file: %s\n", fsPath);
		}
	} else {
		fprintf(stderr, "Insufficient arguments\n");
	}
	return err;
}

int remove(int argc, char **args) {
	auto err = 1;
	if (argc >= 4) {
		auto fsPath = args[2];
		auto inode = ox_atoi(args[3]);
		size_t fsSize;

		auto fsBuff = loadFileBuff(fsPath, &fsSize);
		if (fsBuff) {
			auto fs = createFileSystem(fsBuff);

			if (fs) {
				err = fs->remove(inode);
			} else {
				fprintf(stderr, "Invalid file system.\n");
			}

			if (err) {
				fprintf(stderr, "Could not write to file system.\n");
			} else {
				auto fsFile = fopen(fsPath, "wb");
				if (fsFile) {
					err = fwrite(fsBuff, fsSize, 1, fsFile) != 1;
					err |= fclose(fsFile);
					if (err) {
						fprintf(stderr, "Could not write to file system file.\n");
					}
				} else {
					err = 1;
				}
			}

			delete fs;
			free(fsBuff);
		} else {
			fprintf(stderr, "Could not open file: %s\n", fsPath);
		}
	} else {
		fprintf(stderr, "Insufficient arguments\n");
	}
	return err;
}

int main(int argc, char **args) {
	auto err = 0;
	if (argc > 1) {
		auto cmd = args[1];
		if (ox_strcmp(cmd, "format") == 0) {
			err = format(argc, args);
		} else if (ox_strcmp(cmd, "read") == 0) {
			err = read(argc, args);
		} else if (ox_strcmp(cmd, "write") == 0) {
			err = write(argc, args, false);
		} else if (ox_strcmp(cmd, "write-expand") == 0) {
			err = write(argc, args, true);
		} else if (ox_strcmp(cmd, "compact") == 0) {
			err = compact(argc, args);
		} else if (ox_strcmp(cmd, "rm") == 0) {
			err = remove(argc, args);
		} else if (ox_strcmp(cmd, "help") == 0) {
			printf("%s\n", usage);
		} else if (ox_strcmp(cmd, "version") == 0) {
			printf("oxfstool version %s\n", oxfstoolVersion);
			printf("oxfs format version %d\n", FileStore16::VERSION);
		} else {
			printf("Command '%s' not recognized.\n", cmd);
			err = 1;
		}
	} else {
		printf("%s\n", usage);
	}
	return err;
}
<|endoftext|>