<commit_before>/*

This file is part of VROOM.

Copyright (c) 2015-2021, <NAME>.
All rights reserved (see LICENSE).

*/

#include "structures/vroom/input/input.h"
#include "algorithms/validation/check.h"
#include "problems/cvrp/cvrp.h"
#include "problems/vrptw/vrptw.h"
#include "utils/helpers.h"

namespace vroom {

Input::Input(unsigned amount_size)
  : _start_loading(std::chrono::high_resolution_clock::now()),
    _no_addition_yet(true),
    _has_TW(false),
    _homogeneous_locations(true),
    _geometry(false),
    _has_jobs(false),
    _has_shipments(false),
    _has_custom_matrix(false),
    _all_locations_have_coords(true),
    _amount_size(amount_size),
    _zero(_amount_size) {
}

void Input::set_geometry(bool geometry) {
  _geometry = geometry;
}

void Input::set_routing(std::unique_ptr<routing::Wrapper> routing_wrapper) {
  _routing_wrapper = std::move(routing_wrapper);
}

void Input::check_job(Job& job) {
  // Ensure delivery size consistency.
  const auto& delivery_size = job.delivery.size();
  if (delivery_size != _amount_size) {
    throw Exception(ERROR::INPUT,
                    "Inconsistent delivery length: " +
                      std::to_string(delivery_size) + " instead of " +
                      std::to_string(_amount_size) + '.');
  }

  // Ensure pickup size consistency.
  const auto& pickup_size = job.pickup.size();
  if (pickup_size != _amount_size) {
    throw Exception(ERROR::INPUT,
                    "Inconsistent pickup length: " +
                      std::to_string(pickup_size) + " instead of " +
                      std::to_string(_amount_size) + '.');
  }

  // Ensure that skills are either always or never provided.
  if (_no_addition_yet) {
    _has_skills = !job.skills.empty();
    _no_addition_yet = false;
  } else {
    if (_has_skills != !job.skills.empty()) {
      throw Exception(ERROR::INPUT, "Missing skills.");
    }
  }

  // Check for time-windows.
  _has_TW |= (!(job.tws.size() == 1) or !job.tws[0].is_default());

  if (!job.location.user_index()) {
    // Index of this job in the matrix was not specified upon job
    // creation.
    auto search = _locations_to_index.find(job.location);
    if (search != _locations_to_index.end()) {
      // Using stored index for existing location.
      job.location.set_index(search->second);
    } else {
      // Append new location and store corresponding index.
      auto new_index = _locations.size();
      job.location.set_index(new_index);
      _locations.push_back(job.location);
      _locations_to_index.insert(std::make_pair(job.location, new_index));
    }
  }

  _matrix_used_index.insert(job.index());
  _all_locations_have_coords =
    _all_locations_have_coords && job.location.has_coordinates();
}

void Input::add_job(const Job& job) {
  if (job.type != JOB_TYPE::SINGLE) {
    throw Exception(ERROR::INPUT, "Wrong job type.");
  }
  if (job_id_to_rank.find(job.id) != job_id_to_rank.end()) {
    throw Exception(ERROR::INPUT,
                    "Duplicate job id: " + std::to_string(job.id) + ".");
  }
  job_id_to_rank[job.id] = jobs.size();
  jobs.push_back(job);
  check_job(jobs.back());
  _has_jobs = true;
}

void Input::add_shipment(const Job& pickup, const Job& delivery) {
  if (pickup.priority != delivery.priority) {
    throw Exception(ERROR::INPUT, "Inconsistent shipment priority.");
  }
  if (!(pickup.pickup == delivery.delivery)) {
    throw Exception(ERROR::INPUT, "Inconsistent shipment amount.");
  }
  if (pickup.skills.size() != delivery.skills.size()) {
    throw Exception(ERROR::INPUT, "Inconsistent shipment skills.");
  }
  for (const auto s : pickup.skills) {
    if (delivery.skills.find(s) == delivery.skills.end()) {
      throw Exception(ERROR::INPUT, "Inconsistent shipment skills.");
    }
  }

  if (pickup.type != JOB_TYPE::PICKUP) {
    throw Exception(ERROR::INPUT, "Wrong pickup type.");
  }
  if (pickup_id_to_rank.find(pickup.id) != pickup_id_to_rank.end()) {
    throw Exception(ERROR::INPUT,
                    "Duplicate pickup id: " + std::to_string(pickup.id) + ".");
  }
  pickup_id_to_rank[pickup.id] = jobs.size();
  jobs.push_back(pickup);
  check_job(jobs.back());

  if (delivery.type != JOB_TYPE::DELIVERY) {
    throw Exception(ERROR::INPUT, "Wrong delivery type.");
  }
  if (delivery_id_to_rank.find(delivery.id) != delivery_id_to_rank.end()) {
    throw Exception(ERROR::INPUT,
                    "Duplicate delivery id: " + std::to_string(delivery.id) +
                      ".");
  }
  delivery_id_to_rank[delivery.id] = jobs.size();
  jobs.push_back(delivery);
  check_job(jobs.back());
  _has_shipments = true;
}

void Input::add_vehicle(const Vehicle& vehicle) {
  vehicles.push_back(vehicle);

  auto& current_v = vehicles.back();

  // Ensure amount size consistency.
  const auto& vehicle_amount_size = current_v.capacity.size();
  if (vehicle_amount_size != _amount_size) {
    throw Exception(ERROR::INPUT,
                    "Inconsistent capacity length: " +
                      std::to_string(vehicle_amount_size) + " instead of " +
                      std::to_string(_amount_size) + '.');
  }

  // Ensure that skills are either always or never provided.
  if (_no_addition_yet) {
    _has_skills = !current_v.skills.empty();
    _no_addition_yet = false;
  } else {
    if (_has_skills != !current_v.skills.empty()) {
      throw Exception(ERROR::INPUT, "Missing skills.");
    }
  }

  // Check for time-windows.
  _has_TW = _has_TW || !vehicle.tw.is_default();

  bool has_start = current_v.has_start();
  bool has_end = current_v.has_end();
<commit_msg>Allow empty skills for vehicles.<commit_after>/*

This file is part of VROOM.

Copyright (c) 2015-2021, <NAME>.
All rights reserved (see LICENSE).

*/

#include "structures/vroom/input/input.h"
#include "algorithms/validation/check.h"
#include "problems/cvrp/cvrp.h"
#include "problems/vrptw/vrptw.h"
#include "utils/helpers.h"

namespace vroom {

Input::Input(unsigned amount_size)
  : _start_loading(std::chrono::high_resolution_clock::now()),
    _no_addition_yet(true),
    _has_TW(false),
    _homogeneous_locations(true),
    _geometry(false),
    _has_jobs(false),
    _has_shipments(false),
    _has_custom_matrix(false),
    _all_locations_have_coords(true),
    _amount_size(amount_size),
    _zero(_amount_size) {
}

void Input::set_geometry(bool geometry) {
  _geometry = geometry;
}

void Input::set_routing(std::unique_ptr<routing::Wrapper> routing_wrapper) {
  _routing_wrapper = std::move(routing_wrapper);
}

void Input::check_job(Job& job) {
  // Ensure delivery size consistency.
  const auto& delivery_size = job.delivery.size();
  if (delivery_size != _amount_size) {
    throw Exception(ERROR::INPUT,
                    "Inconsistent delivery length: " +
                      std::to_string(delivery_size) + " instead of " +
                      std::to_string(_amount_size) + '.');
  }

  // Ensure pickup size consistency.
  const auto& pickup_size = job.pickup.size();
  if (pickup_size != _amount_size) {
    throw Exception(ERROR::INPUT,
                    "Inconsistent pickup length: " +
                      std::to_string(pickup_size) + " instead of " +
                      std::to_string(_amount_size) + '.');
  }

  // Ensure that skills are either always or never provided.
  if (_no_addition_yet) {
    _has_skills = !job.skills.empty();
    _no_addition_yet = false;
  } else {
    if (_has_skills != !job.skills.empty()) {
      throw Exception(ERROR::INPUT, "Missing skills.");
    }
  }

  // Check for time-windows.
  _has_TW |= (!(job.tws.size() == 1) or !job.tws[0].is_default());

  if (!job.location.user_index()) {
    // Index of this job in the matrix was not specified upon job
    // creation.
    auto search = _locations_to_index.find(job.location);
    if (search != _locations_to_index.end()) {
      // Using stored index for existing location.
      job.location.set_index(search->second);
    } else {
      // Append new location and store corresponding index.
      auto new_index = _locations.size();
      job.location.set_index(new_index);
      _locations.push_back(job.location);
      _locations_to_index.insert(std::make_pair(job.location, new_index));
    }
  }

  _matrix_used_index.insert(job.index());
  _all_locations_have_coords =
    _all_locations_have_coords && job.location.has_coordinates();
}

void Input::add_job(const Job& job) {
  if (job.type != JOB_TYPE::SINGLE) {
    throw Exception(ERROR::INPUT, "Wrong job type.");
  }
  if (job_id_to_rank.find(job.id) != job_id_to_rank.end()) {
    throw Exception(ERROR::INPUT,
                    "Duplicate job id: " + std::to_string(job.id) + ".");
  }
  job_id_to_rank[job.id] = jobs.size();
  jobs.push_back(job);
  check_job(jobs.back());
  _has_jobs = true;
}

void Input::add_shipment(const Job& pickup, const Job& delivery) {
  if (pickup.priority != delivery.priority) {
    throw Exception(ERROR::INPUT, "Inconsistent shipment priority.");
  }
  if (!(pickup.pickup == delivery.delivery)) {
    throw Exception(ERROR::INPUT, "Inconsistent shipment amount.");
  }
  if (pickup.skills.size() != delivery.skills.size()) {
    throw Exception(ERROR::INPUT, "Inconsistent shipment skills.");
  }
  for (const auto s : pickup.skills) {
    if (delivery.skills.find(s) == delivery.skills.end()) {
      throw Exception(ERROR::INPUT, "Inconsistent shipment skills.");
    }
  }

  if (pickup.type != JOB_TYPE::PICKUP) {
    throw Exception(ERROR::INPUT, "Wrong pickup type.");
  }
  if (pickup_id_to_rank.find(pickup.id) != pickup_id_to_rank.end()) {
    throw Exception(ERROR::INPUT,
                    "Duplicate pickup id: " + std::to_string(pickup.id) + ".");
  }
  pickup_id_to_rank[pickup.id] = jobs.size();
  jobs.push_back(pickup);
  check_job(jobs.back());

  if (delivery.type != JOB_TYPE::DELIVERY) {
    throw Exception(ERROR::INPUT, "Wrong delivery type.");
  }
  if (delivery_id_to_rank.find(delivery.id) != delivery_id_to_rank.end()) {
    throw Exception(ERROR::INPUT,
                    "Duplicate delivery id: " + std::to_string(delivery.id) +
                      ".");
  }
  delivery_id_to_rank[delivery.id] = jobs.size();
  jobs.push_back(delivery);
  check_job(jobs.back());
  _has_shipments = true;
}

void Input::add_vehicle(const Vehicle& vehicle) {
  vehicles.push_back(vehicle);

  auto& current_v = vehicles.back();

  // Ensure amount size consistency.
  const auto& vehicle_amount_size = current_v.capacity.size();
  if (vehicle_amount_size != _amount_size) {
    throw Exception(ERROR::INPUT,
                    "Inconsistent capacity length: " +
                      std::to_string(vehicle_amount_size) + " instead of " +
                      std::to_string(_amount_size) + '.');
  }

  // Check for time-windows and skills.
  _has_TW = _has_TW || !vehicle.tw.is_default();
  _has_skills = _has_skills || !current_v.skills.empty();

  bool has_start = current_v.has_start();
  bool has_end = current_v.has_end();

{'fraction_non_alphanumeric': 0.14184138559708295, 'fraction_numerical': 0.0018231540565177757, 'mean_word_length': 3.310805500982318, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7642869', 'n_tokens_mistral': 3783, 'n_tokens_neox': 3636, 'n_words': 686}