示例#1
0
    bool SplitStringIntoKeyValues(
        const std::string& line,
        char key_value_delimiter,
        std::string* key,
        std::vector<std::string>* values)
    {
        key->clear();
        values->clear();

        // 查找key.
        size_t end_key_pos = line.find_first_of(key_value_delimiter);
        if(end_key_pos == std::string::npos)
        {
            DVLOG(1) << "cannot parse key from line: " << line;
            return false; // 没有key.
        }
        key->assign(line, 0, end_key_pos);

        // 查找values.
        std::string remains(line, end_key_pos, line.size()-end_key_pos);
        size_t begin_values_pos = remains.find_first_not_of(key_value_delimiter);
        if(begin_values_pos == std::string::npos)
        {
            DVLOG(1) << "cannot parse value from line: " << line;
            return false; // 没有value.
        }
        std::string values_string(remains, begin_values_pos,
            remains.size()-begin_values_pos);

        // 添加到vector.
        values->push_back(values_string);
        return true;
    }
示例#2
0
void sax_parser<_Handler,_Config>::cdata()
{
    size_t len = remains();
    assert(len > 3);

    // Parse until we reach ']]>'.
    const char* p0 = m_char;
    size_t i = 0, match = 0;
    for (char c = cur_char(); i < len; ++i, c = next_char())
    {
        if (c == ']')
        {
            // Be aware that we may encounter a series of more than two ']'
            // characters, in which case we'll only count the last two.

            if (match == 0)
                // First ']'
                ++match;
            else if (match == 1)
                // Second ']'
                ++match;
        }
        else if (c == '>' && match == 2)
        {
            // Found ']]>'.
            size_t cdata_len = i - 2;
            m_handler.characters(pstring(p0, cdata_len), false);
            next();
            return;
        }
        else
            match = 0;
    }
    throw sax::malformed_xml_error("malformed CDATA section.");
}
示例#3
0
void parser_base::comment()
{
    // Parse until we reach '-->'.
    size_t len = remains();
    assert(len > 3);
    char c = cur_char();
    size_t i = 0;
    bool hyphen = false;
    for (; i < len; ++i, c = next_char())
    {
        if (c == '-')
        {
            if (!hyphen)
                // first hyphen.
                hyphen = true;
            else
                // second hyphen.
                break;
        }
        else
            hyphen = false;
    }

    if (len - i < 2 || next_char() != '>')
        throw malformed_xml_error("'--' should not occur in comment other than in the closing tag.");

    next();
}
示例#4
0
// Parse a string of the form [~]<lang>[+[~]<lang>]*.
// Langs with no prefix get appended to to_load, provided they
// are not in there already.
// Langs with ~ prefix get appended to not_to_load, provided they are not in
// there already.
void Tesseract::ParseLanguageString(const char* lang_str,
                                    GenericVector<STRING>* to_load,
                                    GenericVector<STRING>* not_to_load) {
  STRING remains(lang_str);
  while (remains.length() > 0) {
    // Find the start of the lang code and which vector to add to.
    const char* start = remains.string();
    while (*start == '+')
      ++start;
    GenericVector<STRING>* target = to_load;
    if (*start == '~') {
      target = not_to_load;
      ++start;
    }
    // Find the index of the end of the lang code in string start.
    int end = strlen(start);
    const char* plus = strchr(start, '+');
    if (plus != NULL && plus - start < end)
      end = plus - start;
    STRING lang_code(start);
    lang_code.truncate_at(end);
    STRING next(start + end);
    remains = next;
    // Check whether lang_code is already in the target vector and add.
    if (!IsStrInList(lang_code, *target)) {
      if (tessdata_manager_debug_level)
        tprintf("Adding language '%s' to list\n", lang_code.string());
      target->push_back(lang_code);
    }
  }
}
示例#5
0
void dot_t::schedule_tick()
{
  if ( sim.debug )
    sim.out_debug.printf( "%s schedules tick for %s on %s", source -> name(), name(), target -> name() );

  time_to_tick = current_action -> tick_time( state -> haste );
  assert( time_to_tick > timespan_t::zero() && "A Dot needs a positive tick time!" );

  // Recalculate num_ticks:
  num_ticks = current_tick + as<int>(std::ceil( remains() / time_to_tick ));
  // NOTE: Last tick factor has to be computed after time_to_tick is set (and after the dot has an
  // end event).
  last_tick_factor = current_action -> last_tick_factor( this, time_to_tick, remains() );

  tick_event = new ( sim ) dot_tick_event_t( this, time_to_tick );

  if ( current_action -> channeled )
  {
    // FIXME: Find some way to make this more realistic - the actor shouldn't have to recast quite this early
    // Response: "Have to"?  It might be good to recast early - since the GCD will end sooner. Depends on the situation. -ersimont
    expr_t* expr = current_action -> early_chain_if_expr;
    if ( ( ( current_action -> chain && current_tick + 1 == num_ticks )
           || ( current_tick > 0
                && expr
                && expr -> success()
                && current_action -> player -> gcd_ready <= sim.current_time() ) )
         && current_action -> ready()
         && !is_higher_priority_action_available() )
    {
      // FIXME: We can probably use "source" instead of "action->player"

      current_action -> player -> channeling = 0;
      current_action -> player -> gcd_ready = sim.current_time() + current_action -> gcd();
      current_action -> execute();
      if ( current_action -> result_is_hit( current_action -> execute_state -> result ) )
      {
        current_action -> player -> channeling = current_action;
      }
      else
        cancel();
    }
    else
    {
      current_action -> player -> channeling = current_action;
    }
  }
}
示例#6
0
shared_ptr<map<Resource *, int>> Schedule :: resourceRemain(int timeMoment) const
{
    shared_ptr<map<Resource *, int>> remains(new map<Resource *, int>());
    for (auto &pResourceRemains : _resourceRemains) {
        (*remains)[pResourceRemains.first] = (*pResourceRemains.second)[timeMoment];
    }
    return remains;
}
示例#7
0
LocalityManager::LocalityManager(const Configuration &config)
	: m_max_concurrency(config.max_concurrency())
	, m_numa_node_count(1)
	, m_thread_mapping()
	, m_partition_mapping()
	, m_node_to_worker()
{
	if(config.affinity() == AffinityMode::NONE){ return; }
	const auto num_threads = config.max_concurrency();
	const auto num_partitions = config.partition_count();
	auto &topo = Topology::instance();
	m_numa_node_count = topo.numa_node_count();
	m_node_to_worker.assign(m_numa_node_count, std::vector<identifier_type>());
	std::vector<size_type> pu_per_node(m_numa_node_count);
	for(identifier_type i = 0; i < m_numa_node_count; ++i){
		pu_per_node[i] = topo.processing_unit_per_node(i);
	}
	if(config.affinity() == AffinityMode::COMPACT){
		identifier_type node = 0;
		for(identifier_type i = 0, j = 0; i < num_threads; ++i){
			while(j == pu_per_node[node]){
				j = 0;
				node = (node + 1) % m_numa_node_count;
			}
			m_thread_mapping.push_back(node);
			m_node_to_worker[node].push_back(i);
			++j;
		}
	}else if(config.affinity() == AffinityMode::SCATTER){
		std::vector<size_type> remains(pu_per_node);
		const size_type total_pu_count =
			std::accumulate(pu_per_node.begin(), pu_per_node.end(), 0);
		identifier_type node = 0;
		for(identifier_type i = 0; i < num_threads; ++i){
			if(i % total_pu_count == 0){
				remains = pu_per_node;
				node = 0;
			}
			while(remains[node] == 0){
				node = (node + 1) % m_numa_node_count;
			}
			m_thread_mapping.push_back(node);
			m_node_to_worker[node].push_back(i);
			--remains[node];
			node = (node + 1) % m_numa_node_count;
		}
	}
	for(identifier_type i = 0; i < num_partitions; ++i){
		m_partition_mapping.push_back(
			m_thread_mapping[i % num_threads]);
	}
}
示例#8
0
void RectBinArrange::Arrange(const std::vector<ee::ImageSprite*>& sprs)
{
// 	std::vector<ee::ImageSprite*> sorted(sprs);
// 	sortByMaxEdge(sorted);
// 
// 	std::vector<RectSize> input;
// 	BeforePacking(sorted, input);
// 
// 	std::vector<Rect> output;
// 	GuillotineBinPackAlg(input, output);
// // 	MaxRectsBinPackAlg(input, output);
// // 	ShelfBinPackAlg(input, output);
// // 	SkylineBinPackAlg(input, output);
// 	
// 	AfterPacking(sorted, output);

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

	m_tex_account = 0;

	std::vector<ee::ImageSprite*> sorted(sprs);
	SortByMaxEdge(sorted);

	int count = 0;

	float x_offset = 0;
	std::vector<ee::ImageSprite*> remains(sorted);
	while (!remains.empty())
	{
		std::vector<RectSize> input;
		BeforePacking(remains, input);

		std::vector<Rect> output;
		GuillotineBinPackAlg(input, output);
		// 	MaxRectsBinPackAlg(input, output);
		// 	ShelfBinPackAlg(input, output);
		// 	SkylineBinPackAlg(input, output);

		std::vector<ee::ImageSprite*> _remains;
		AfterPacking(x_offset, remains, output, _remains);
		remains = _remains;

		x_offset += Context::Instance()->width * TEXTURE_X_OFFSET_FACTOR;

		m_tex_account++;

		if (count >= 100) {
			break;
		}
		++count;
	}
}
示例#9
0
void dot_t::check_tick_zero()
{
  // If we're precasting a helpful dot and we're not in combat, fake precasting by using a first tick.
  // We also reduce the duration by one tick interval in action_t::trigger_dot().
  bool fake_first_tick = ! current_action -> harmful && ! current_action -> player -> in_combat;

  if ( current_action -> tick_zero || fake_first_tick )
  {
    timespan_t previous_ttt = time_to_tick;
    time_to_tick = timespan_t::zero();
    // Recalculate num_ticks:
    timespan_t tick_time = current_action->tick_time(state->haste);
    assert( tick_time > timespan_t::zero() && "A Dot needs a positive tick time!" );
    num_ticks = current_tick + as<int>(std::ceil(remains() / tick_time ) );
    tick_zero();
    if ( remains() <= timespan_t::zero() )
    {
      last_tick();
      return;
    }
    time_to_tick = previous_ttt;
  }
}
示例#10
0
void parser_base::skip_bom()
{
    if (remains() < 4)
        // Stream too short to have a byte order mark.
        return;

    // 0xef 0xbb 0 xbf is the UTF-8 byte order mark
    unsigned char c = static_cast<unsigned char>(cur_char());
    if (c != '<')
    {
        if (c != 0xef || static_cast<unsigned char>(next_char()) != 0xbb ||
            static_cast<unsigned char>(next_char()) != 0xbf || next_char() != '<')
            throw malformed_xml_error("unsupported encoding. only 8 bit encodings are supported");
    }
}
示例#11
0
void cooldown_t::set_recharge_multiplier( double v )
{
  assert( v >= 0.0 && "Cooldown recharge multiplier should probably not be negative." );

  if ( up() )
  {
    // Cooldown not active, just set the recharge multiplier.
    recharge_multiplier = v;
  }
  else
  {
    // Cooldown up, we need to recalculate remaining duration.
    timespan_t new_leftover_adjust = remains() * ( v / recharge_multiplier - 1.0  );
    ready += new_leftover_adjust;
    recharge_multiplier = v;
  }
}
示例#12
0
void sax_parser<_Handler,_Config>::special_tag()
{
    assert(cur_char() == '!');
    // This can be either <![CDATA, <!--, or <!DOCTYPE.
    size_t len = remains();
    if (len < 2)
        throw sax::malformed_xml_error("special tag too short.");

    switch (next_char())
    {
        case '-':
        {
            // Possibly comment.
            if (next_char() != '-')
                throw sax::malformed_xml_error("comment expected.");

            len -= 2;
            if (len < 3)
                throw sax::malformed_xml_error("malformed comment.");

            next();
            comment();
        }
        break;
        case '[':
        {
            // Possibly a CDATA.
            expects_next("CDATA[", 6);
            if (has_char())
                cdata();
        }
        break;
        case 'D':
        {
            // check if this is a DOCTYPE.
            expects_next("OCTYPE", 6);
            blank();
            if (has_char())
                doctype();
        }
        break;
        default:
            throw sax::malformed_xml_error("failed to parse special tag.");
    }
}
示例#13
0
void parser_base::expects_next(const char* p, size_t n)
{
    if (remains() < n+1)
        throw malformed_xml_error("not enough stream left to check for an expected string segment.");

    const char* p0 = p;
    const char* p_end = p + n;
    char c = next_char();
    for (; p != p_end; ++p, c = next_char())
    {
        if (c == *p)
            continue;

        std::ostringstream os;
        os << "'" << std::string(p0, n) << "' was expected, but not found.";
        throw malformed_xml_error("sadf");
    }
}
示例#14
0
void StringMessage::doDeserializeChunk(const data_chunk& data)
{
	this->append(data.begin(), data.end());
	size_t found = this->find(itsTerminator);

	if (std::string::npos != found) {
		// Terminator was found in data
		size_t remains_pos = found + itsTerminator.size();
		data_chunk remains(this->begin() + remains_pos, this->end());
		this->erase(this->begin() + remains_pos, this->end());
		DeserializingComplete(remains);
		DBG << "Completed string with " << this->size()
			<< "B (" << found << " + " << itsTerminator.size() << ")" << std::endl;
	} else {
		// Terminator was not found in data
		DBG << "Continuing, " << this->size() << "B so far" << std::endl;
	}
}
示例#15
0
void handle_waterwheel_destruction(OBJ_DATA & obj)
{
    // Verify a room
    ROOM_INDEX_DATA * room(get_room_for_obj(obj));
    if (room == NULL)
        return;

    // Load up the remains and transfer the contents
    OBJ_DATA * remains(create_object(get_obj_index(OBJ_VNUM_WATERWHEEL_REMAINS), 0));
    remains->level = obj.level;

    for (OBJ_DATA * item(obj.contains); item != NULL; item = obj.contains)
    {
        obj_from_obj(item);
        obj_to_obj(item, remains);
    }

    obj_to_room(remains, room);
}
示例#16
0
/* Precondition: ticking == true
 */
void dot_t::refresh( timespan_t duration )
{
  current_duration = current_action -> calculate_dot_refresh_duration( this, duration );

  last_start = sim.current_time();

  assert( end_event && "Dot is ticking but has no end event." );
  timespan_t remaining_duration = end_event -> remains();

  // DoT Refresh events have to be canceled instead of refreshed. Otherwise, it
  // is possible to get the dot into a state, where the last reschedule event
  // occurs between the second to last, and the last tick. This will cause the
  // event ordering in the sim to flip (last tick happens before end event),
  // causing the dot to tick twice at the end of the duration.
  event_t::cancel( end_event );
  end_event = new ( sim ) dot_end_event_t( this, current_duration );

  check_tick_zero();

  // Recalculate num_ticks:
  num_ticks = current_tick + as<int>(std::ceil(remains() / current_action->tick_time(state->haste) ) );

  if ( sim.debug )
    sim.out_debug.printf( "%s refreshes dot for %s on %s. duration=%.3f",
                          source -> name(), name(), target -> name(),
                          current_duration.total_seconds() );

  // Ensure that the ticker is running when dots are refreshed. It is possible
  // that a specialized dot handling cancels the ticker when there's no time to
  // tick another one (before the periodic effect ends). This is for example
  // used in the Rogue module to implement Sinister Calling.
  if ( ! tick_event )
  {
    assert( ! current_action -> channeled );
    tick_event = new ( sim ) dot_tick_event_t( this, remaining_duration );
  }
}
示例#17
0
void dot_t::extend_duration( timespan_t extra_seconds, timespan_t max_total_time, uint32_t state_flags )
{
  if ( ! ticking )
    return;

  if ( state_flags == ( uint32_t ) - 1 ) state_flags = current_action -> snapshot_flags;

  // Make sure this DoT is still ticking......
  assert( tick_event );
  assert( state );
  current_action -> snapshot_internal( state, state_flags, current_action -> type == ACTION_HEAL ? HEAL_OVER_TIME : DMG_OVER_TIME );

  if ( max_total_time > timespan_t::zero() )
  {
    timespan_t over_cap = remains() + extra_seconds - max_total_time;
    if ( over_cap > timespan_t::zero() )
      extra_seconds -= over_cap;
  }
  current_duration += extra_seconds;
  extended_time += extra_seconds;


  if ( sim.log )
  {
    sim.out_log.printf( "%s extends duration of %s on %s by %.1f second(s).",
                source -> name(), name(), target -> name(), extra_seconds.total_seconds() );
  }

  assert( end_event && "Dot is ticking but has no end event." );
  timespan_t remains = end_event -> remains();
  remains += extra_seconds;
  if ( remains != end_event -> remains() )
    end_event -> reschedule( remains );

  current_action -> stats -> add_refresh( state -> target );
}
/**
 *  寻找M满足N × M只包含0和1
 *  采用http://blog.csdn.net/jcwkyl/article/details/3859155的思路
 *  最后的值可能非常大,long long可能容不下
 */
long long findMultiplier(long long N)
{
    if (N <= 0) {
        return 0;
    }
    vector<bool> remains(N, false);
    queue<long long> Q;
    Q.push(1);
    // 层序的方式遍历,广度优先
    while (!Q.empty()) {
        long long root = Q.front();
        Q.pop();
        // 利用同余的性质进行剪枝
        if (remains[root % N]) {
            continue;
        }
        if (root % N == 0) {
            return root;
        }
        remains[root % N] = true;
        Q.push(root * 10);
        Q.push(root * 10 + 1);
    }
}
示例#19
0
void sax_parser<_Handler,_Config>::doctype()
{
    // Parse the root element first.
    sax::doctype_declaration param;
    name(param.root_element);
    blank();

    // Either PUBLIC or SYSTEM.
    size_t len = remains();
    if (len < 6)
        sax::malformed_xml_error("DOCTYPE section too short.");

    param.keyword = sax::doctype_declaration::keyword_private;
    char c = cur_char();
    if (c == 'P')
    {
        if (next_char() != 'U' || next_char() != 'B' || next_char() != 'L' || next_char() != 'I' || next_char() != 'C')
            throw sax::malformed_xml_error("malformed DOCTYPE section.");

        param.keyword = sax::doctype_declaration::keyword_public;
    }
    else if (c == 'S')
    {
        if (next_char() != 'Y' || next_char() != 'S' || next_char() != 'T' || next_char() != 'E' || next_char() != 'M')
            throw sax::malformed_xml_error("malformed DOCTYPE section.");
    }

    next_check();
    blank();
    has_char_throw("DOCTYPE section too short.");

    // Parse FPI.
    value(param.fpi, false);

    has_char_throw("DOCTYPE section too short.");
    blank();
    has_char_throw("DOCTYPE section too short.");

    if (cur_char() == '>')
    {
        // Optional URI not given. Exit.
#if ORCUS_DEBUG_SAX_PARSER
        cout << "sax_parser::doctype: root='" << param.root_element << "', fpi='" << param.fpi << "'" << endl;
#endif
        m_handler.doctype(param);
        next();
        return;
    }

    // Parse optional URI.
    value(param.uri, false);

    has_char_throw("DOCTYPE section too short.");
    blank();
    has_char_throw("DOCTYPE section too short.");

    if (cur_char() != '>')
        throw sax::malformed_xml_error("malformed DOCTYPE section - closing '>' expected but not found.");

#if ORCUS_DEBUG_SAX_PARSER
    cout << "sax_parser::doctype: root='" << param.root_element << "', fpi='" << param.fpi << "' uri='" << param.uri << "'" << endl;
#endif
    m_handler.doctype(param);
    next();
}
示例#20
0
文件: main.c 项目: luoxiaojian/home
int algo()
{
	int i, j;
	int RWmP[MAX_TASK_NUM];
	fscanf(fin, "%d%d%d%d", &true_task_num, &task_num, &processor_num, &lcm_period);

	period=(int *)malloc(sizeof(int)*task_num);
	execute=(int *)malloc(sizeof(int)*task_num);
	w=(float *)malloc(sizeof(float)*task_num);
	for(i=0; i<task_num; i++)
		fscanf(fin, "%d", &period[i]);
	for(i=0; i<task_num; i++)
		fscanf(fin, "%d", &execute[i]);

	for(i=0; i<task_num; i++)
	{
		w[i]=((float)execute[i])/((float)period[i]);
		RWmP[i]=0;
	}

	calc_bound();
	initialize();
	calc_alpha();
	calc_UF();
	
	for(i=0; i<processor_num; i++)
		for(j=0; j<lcm_period; j++)
			alloc[i][j]=-1;
	
	for(i=0; i<row_num; i++)
		Bfair(RWmP, i);

/* 	for(i=0; i<processor_num; i++)
	{
		for(j=0; j<lcm_period; j++)
		{
			fprintf(fout, "%d ", alloc[i][j]);
		}
		fprintf(fout, "\n");
	}*/
	if(checkSchedule()==-1)
	{
		printf("!!!Schedule error.\n");
	}
	else
		printf("successfully scheduling.\n");

	countPreemption();
	countMigration();
	countEvent();

	fprintf(flog, "preemption:\n");
	for(i=0; i<true_task_num; i++)
	{
		double ppj=((double)preemption[i])/((double)(lcm_period/period[i]));
		fprintf(flog, "%lf ", ppj);
	}
	fprintf(flog, "\n");
	fprintf(flog, "migration:\n");
	for(i=0; i<true_task_num; i++)
	{
		double mpj=((double)migration[i])/((double)(lcm_period/period[i]));
		fprintf(flog, "%lf ", mpj);
	}
	fprintf(flog, "\n");
	fprintf(flog, "event:\n");
	for(i=0; i<true_task_num; i++)
	{
		double epj=((double)event[i])/((double)(lcm_period/period[i]));
		fprintf(flog, "%lf ", epj);
	}
	fprintf(flog, "\n");

	remains();
	return 0;
}
示例#21
0
/* Returns the ticks left based on the current estimated number of max ticks minus elapsed ticks.
 * Beware: This value may change over time, giving higher or lower values depending on the tick time of the dot!
 */
int dot_t::ticks_left() const
{
  if ( ! current_action ) return 0;
  if ( ! ticking ) return 0;
  return static_cast<int>( std::ceil( remains() / current_action -> tick_time( state -> haste ) ) );
}
示例#22
0
void dot_t::copy( player_t* other_target, dot_copy_e copy_type )
{
  if ( target == other_target )
    return;

  dot_t* other_dot = current_action -> get_dot( other_target );
  // Copied dot, with the DOT_COPY_START method cancels the ongoing dot on the
  // target, and then starts a fresh dot on it with the source dot's (copied)
  // state
  if ( copy_type == DOT_COPY_START && other_dot -> is_ticking() )
    other_dot -> cancel();

  // Shared initialize for the target dot state, independent of the copying
  // method
  action_state_t* target_state = 0;
  if ( ! other_dot -> state )
  {
    target_state = current_action -> get_state( state );
    other_dot -> state = target_state;
  }
  else
  {
    target_state = other_dot -> state;
    target_state -> copy_state( state );
  }
  target_state -> target = other_dot -> target;
  target_state -> action = current_action;

  other_dot -> current_action = current_action;

  // Default behavior simply copies the current stat of the source dot
  // (including duration) to the target and starts it
  if ( copy_type == DOT_COPY_START )
  {
    other_dot -> trigger( current_duration );
  }
  // If we don't start the copied dot from the beginning, we need to bypass a
  // lot of the dot scheduling logic, and simply do the minimum amount possible
  // to get the dot aligned with the source dot, both in terms of state, as
  // well as the remaining duration, and the remaining ongoing tick time.
  else
  {
    timespan_t new_duration;

    // Other dot is ticking, the cloning process will be a refresh
    if ( other_dot -> is_ticking() )
    {
      // The new duration is computed through our normal refresh duration
      // method. End result (by default) will be source_remains + min(
      // target_remains, 0.3 * source_remains )
      new_duration = current_action -> calculate_dot_refresh_duration( other_dot, remains() );

      assert( other_dot -> end_event && other_dot -> tick_event );

      // Cancel target's ongoing events, we are about to re-do them
      event_t::cancel( other_dot -> end_event );
      event_t::cancel( other_dot -> tick_event );
    }
    // No target dot ticking, just copy the source's remaining time
    else
    {
      new_duration = remains();

      // Add an active dot on the source, since we are starting a new one
      source -> add_active_dot( current_action -> internal_id );
    }

    if ( sim.debug )
      sim.out_debug.printf( "%s cloning %s from %s to %s: source_remains=%.3f target_remains=%.3f target_duration=%.3f",
        current_action -> player -> name(), current_action -> name(), target -> name(), other_target -> name(),
        remains().total_seconds(), other_dot -> remains().total_seconds(), new_duration.total_seconds() );

    // To compute new number of ticks, we use the new duration, plus the
    // source's ongoing remaining tick time, since we are copying the ongoing
    // tick too
    timespan_t computed_tick_duration = new_duration;
    if ( tick_event && tick_event -> remains() > new_duration )
      computed_tick_duration += time_to_tick - tick_event -> remains();

    // Aand then adjust some things for ease-of-use for now. The copied dot has
    // its current tick reset to 0, and it's last start time is set to current
    // time.
    //
    // TODO?: A more proper way might be to also copy the source dot's last
    // start time and current tick, in practice it is probably meaningless,
    // though.
    other_dot -> last_start = sim.current_time();
    other_dot -> current_duration = new_duration;
    other_dot -> current_tick = 0;
    other_dot -> extended_time = timespan_t::zero();
    other_dot -> time_to_tick = time_to_tick;
    other_dot -> num_ticks = as<int>( std::ceil( computed_tick_duration / time_to_tick ) );

    other_dot -> ticking = true;
    other_dot -> end_event = new ( sim ) dot_end_event_t( other_dot, new_duration );

    other_dot -> last_tick_factor = other_dot -> current_action -> last_tick_factor( other_dot, time_to_tick, computed_tick_duration );

    // The clone may happen on tick, or mid tick. If it happens on tick, the
    // source dot will not have a new tick event scheduled yet, so the tick
    // time has to be based on the action's tick time. If cloning happens mid
    // tick, we just use the remaining tick time of the source for the tick
    // time. Tick time will be recalculated on the next tick, implicitly
    // syncing it to the source's tick time.
    timespan_t tick_time;
    if ( tick_event )
      tick_time = tick_event -> remains();
    else
      tick_time = other_dot -> current_action -> tick_time( other_dot -> state -> haste );

    other_dot -> tick_event = new ( sim ) dot_tick_event_t( other_dot, tick_time );
  }
}