<commit_before><?php declare(strict_types=1);
/**
 * Hummingbird Anime List Client
 *
 * An API client for Kitsu to manage anime and manga watch lists
 *
 * PHP version 7.4
 *
 * @package     HummingbirdAnimeClient
 * @author      <NAME> <<EMAIL>>
 * @copyright   2015 - 2020  <NAME>
 * @license     http://www.opensource.org/licenses/mit-license.html  MIT License
 * @version     5
 * @link        https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
 */

namespace Aviat\AnimeClient\API\Kitsu\Transformer;

use Aviat\AnimeClient\API\Mapping\AnimeWatchingStatus;
use Aviat\AnimeClient\Types\HistoryItem;
use Aviat\Ion\Di\ContainerAware;

class AnimeHistoryTransformer {
	use ContainerAware;

	protected array $skipList = [];

	/**
	 * Convert raw history
	 *
	 * @param array $data
	 * @return array
	 */
	public function transform(array $data): array
	{
		$output = [];

		foreach ($data as $id => $entry)
		{
			if ( ! isset($entry['relationships']['anime']))
			{
				continue;
			}

			if (in_array($id, $this->skipList, FALSE))
			{
				continue;
			}

			if ($entry['attributes']['kind'] === 'progressed')
			{
				$output[] = $this->transformProgress($entry);
			}
			else if ($entry['attributes']['kind'] === 'updated')
			{
				$output[] = $this->transformUpdated($entry);
			}
		}

		return $this->aggregate($output);
	}

	/**
	 * Combine consecutive 'progressed' events
	 *
	 * @param array $singles
	 * @return array
	 */
	protected function aggregate (array $singles): array
	{
		$output = [];

		$count = count($singles);
		for ($i = 0; $i < $count; $i++)
		{
			$entry = $singles[$i];
			$prevTitle = $entry['title'];
			$nextId = $i + 1;
			if ($nextId < $count)
			{
				$entries = [];
				$next = $singles[$nextId];
				while (
					$next['kind'] === 'progressed' &&
					$next['title'] === $prevTitle
				) {
					$entries[] = $next;
					$prevTitle = $next['title'];

					if ($nextId + 1 < $count)
					{
						$nextId++;
						$next = $singles[$nextId];
					}
					else
					{
						break;
					}
				}
			}

			if (count($entries) > 1)
			{
				$episodes = [];

				foreach ($entries as $e)
				{
					$episodes[] = max($e['original']['attributes']['changedData']['progress']);
				}
				$firstEpisode = min($episodes);
				$lastEpisode = max($episodes);

				$title = $entries[0]['title'];

				// Get rid of the single entry added before aggregating
				// array_pop($output);

				$action = (count($entries) > 3)
					? "Marathoned episodes {$firstEpisode}-{$lastEpisode} of {$title}"
					: "Watched episodes {$firstEpisode}-{$lastEpisode} of {$title}";

				$output[] = HistoryItem::check([
					'title' => $title,
					'action' => $action,
					'coverImg' => $entries[0]['coverImg'],
					'isAggregate' => true,
					'updated' => $entries[0]['updated'],
				]);

				// Skip the rest of the aggregate in the main loop
				$i += count($entries);
				$prevTitle = $title;
				continue;
			}
			else
			{
				$prevTitle = $entry['title'];
				$output[] = $entry;
			}

		}

		return $output;
	}

	protected function transformProgress ($entry): array
	{
		$animeId = array_keys($entry['relationships']['anime'])[0];<commit_msg>Fix off-by-one errors<commit_after><?php declare(strict_types=1);
/**
 * Hummingbird Anime List Client
 *
 * An API client for Kitsu to manage anime and manga watch lists
 *
 * PHP version 7.4
 *
 * @package     HummingbirdAnimeClient
 * @author      <NAME> <<EMAIL>>
 * @copyright   2015 - 2020  <NAME>
 * @license     http://www.opensource.org/licenses/mit-license.html  MIT License
 * @version     5
 * @link        https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
 */

namespace Aviat\AnimeClient\API\Kitsu\Transformer;

use Aviat\AnimeClient\API\Mapping\AnimeWatchingStatus;
use Aviat\AnimeClient\Types\HistoryItem;
use Aviat\Ion\Di\ContainerAware;

class AnimeHistoryTransformer {
	use ContainerAware;

	protected array $skipList = [];

	/**
	 * Convert raw history
	 *
	 * @param array $data
	 * @return array
	 */
	public function transform(array $data): array
	{
		$output = [];

		foreach ($data as $id => $entry)
		{
			if ( ! isset($entry['relationships']['anime']))
			{
				continue;
			}

			if (in_array($id, $this->skipList, FALSE))
			{
				continue;
			}

			if ($entry['attributes']['kind'] === 'progressed')
			{
				$output[] = $this->transformProgress($entry);
			}
			else if ($entry['attributes']['kind'] === 'updated')
			{
				$output[] = $this->transformUpdated($entry);
			}
		}

		return $this->aggregate($output);
	}

	/**
	 * Combine consecutive 'progressed' events
	 *
	 * @param array $singles
	 * @return array
	 */
	protected function aggregate (array $singles): array
	{
		$output = [];

		$count = count($singles);
		for ($i = 0; $i < $count; $i++)
		{
			$entries = [];
			$entry = $singles[$i];
			$prevTitle = $entry['title'];
			$nextId = $i;
			$next = $singles[$nextId];
			while (
				$next['kind'] === 'progressed' &&
				$next['title'] === $prevTitle
			) {
				$entries[] = $next;
				$prevTitle = $next['title'];

				if ($nextId + 1 < $count)
				{
					$nextId++;
					$next = $singles[$nextId];
					continue;
				}

				break;
			}

			if (count($entries) > 1)
			{
				$episodes = [];

				foreach ($entries as $e)
				{
					$episodes[] = max($e['original']['attributes']['changedData']['progress']);
				}
				$firstEpisode = min($episodes);
				$lastEpisode = max($episodes);

				$title = $entries[0]['title'];

				$action = (count($entries) > 3)
					? "Marathoned episodes {$firstEpisode}-{$lastEpisode} of {$title}"
					: "Watched episodes {$firstEpisode}-{$lastEpisode} of {$title}";

				$output[] = HistoryItem::check([
					'title' => $title,
					'action' => $action,
					'coverImg' => $entries[0]['coverImg'],
					'isAggregate' => true,
					'updated' => $entries[0]['updated'],
				]);

				// Skip the rest of the aggregate in the main loop
				$i += count($entries) - 1;
				continue;
			}
			else
			{
				$output[] = $entry;
			}

		}

		return $output;
	}

	protected function transformProgress ($entry): array
	{
		$animeId = array_keys($entry['relationships']['anime'])[0];
{'fraction_non_alphanumeric': 0.18113577023498695, 'fraction_numerical': 0.007180156657963447, 'mean_word_length': 3.013752455795678, 'pattern_counts': {'":': 0, '<': 18, '<?xml version=': 0, '>': 35, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5527268', 'n_tokens_mistral': 2569, 'n_tokens_neox': 2052, 'n_words': 440}