<commit_before>// Copyright © 2015 HSL <https://www.hsl.fi>
// This program is dual-licensed under the EUPL v1.2 and AGPLv3 licenses.

package fi.hsl.parkandride.core.domain.prediction;

import fi.hsl.parkandride.core.back.PredictionRepository;
import fi.hsl.parkandride.core.domain.Utilization;
import org.joda.time.DateTime;
import org.joda.time.Weeks;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class AverageOfPreviousWeeksPredictor implements Predictor {

    public static final String TYPE = "average-of-previous-weeks";

    @Override
    public String getType() {
        return TYPE;
    }

    @Override
    public List<Prediction> predict(PredictorState state, UtilizationHistory history) {
        Utilization latest = history.getLatest();
        DateTime now = latest.timestamp;

        List<List<Utilization>> groupedByWeek = Stream.of(Weeks.weeks(1), Weeks.weeks(2), Weeks.weeks(3))
                .map(weeks -> {
                    DateTime start = now.minus(weeks);
                    DateTime end = start.plus(PredictionRepository.PREDICTION_WINDOW);
                    return history.getRange(start, end);
                    // TODO: make timestamps relative to start
                })
                .collect(Collectors.toList());

        List<List<Utilization>> groupedByTimeOfDay = transpose(groupedByWeek);

        return groupedByTimeOfDay.stream()
                .map(this::toPrediction)
                .collect(Collectors.toList());
    }

    private Prediction toPrediction(List<Utilization> utilizations) {
        DateTime timestamp = utilizations.get(0).timestamp.plusWeeks(1); // XXX: shift time correctly
        int spacesAvailable = (int) Math.round(utilizations.stream()
                .mapToInt(u -> u.spacesAvailable)
                .average()
                .getAsDouble());
        return new Prediction(timestamp, spacesAvailable);
    }

    private static List<List<Utilization>> transpose(List<List<Utilization>> sources) {
        List<Iterator<Utilization>> iterators = sources.stream()
                .map(List::iterator)
                .collect(Collectors.toList());
        List<List<Utilization>> results = new ArrayList<>();
        while (hasNexts(iterators)) {
            results.add(nexts(iterators));
        }
        return results;
    }

    private static boolean hasNexts(List<Iterator<Utilization>> heads) {
        return heads.stream().anyMatch(Iterator::hasNext);
    }

    private static List<Utilization> nexts(List<Iterator<Utilization>> heads) {
        return heads.stream()
                .filter(Iterator::hasNext)
                .map(Iterator::next)
                .collect(Collectors.toList());
    }
}
<commit_msg>refactor(prediction): AverageOfPreviousWeeksPredictor without hacks<commit_after>// Copyright © 2015 HSL <https://www.hsl.fi>
// This program is dual-licensed under the EUPL v1.2 and AGPLv3 licenses.

package fi.hsl.parkandride.core.domain.prediction;

import fi.hsl.parkandride.core.back.PredictionRepository;
import fi.hsl.parkandride.core.domain.Utilization;
import org.joda.time.DateTime;
import org.joda.time.Weeks;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class AverageOfPreviousWeeksPredictor implements Predictor {

    public static final String TYPE = "average-of-previous-weeks";

    @Override
    public String getType() {
        return TYPE;
    }

    @Override
    public List<Prediction> predict(PredictorState state, UtilizationHistory history) {
        Utilization latest = history.getLatest();
        DateTime now = latest.timestamp;

        List<List<Prediction>> groupedByWeek = Stream.of(Weeks.weeks(1), Weeks.weeks(2), Weeks.weeks(3))
                .map(offset -> {
                    DateTime start = now.minus(offset);
                    DateTime end = start.plus(PredictionRepository.PREDICTION_WINDOW);
                    List<Utilization> utilizations = history.getRange(start, end);
                    return utilizations.stream()
                            .map(u -> new Prediction(u.timestamp.plus(offset), u.spacesAvailable))
                            .collect(Collectors.toList());
                })
                .collect(Collectors.toList());

        List<List<Prediction>> groupedByTimeOfDay = transpose(groupedByWeek);

        return groupedByTimeOfDay.stream()
                .map(this::reduce)
                .collect(Collectors.toList());
    }

    private Prediction reduce(List<Prediction> predictions) {
        DateTime timestamp = predictions.get(0).timestamp;
        int spacesAvailable = (int) Math.round(predictions.stream()
                .mapToInt(u -> u.spacesAvailable)
                .average()
                .getAsDouble());
        return new Prediction(timestamp, spacesAvailable);
    }

    private static <T> List<List<T>> transpose(List<List<T>> sources) {
        List<Iterator<T>> iterators = sources.stream()
                .map(List::iterator)
                .collect(Collectors.toList());
        List<List<T>> results = new ArrayList<>();
        while (hasNexts(iterators)) {
            results.add(nexts(iterators));
        }
        return results;
    }

    private static <T> boolean hasNexts(List<Iterator<T>> heads) {
        return heads.stream().anyMatch(Iterator::hasNext);
    }

    private static <T> List<T> nexts(List<Iterator<T>> heads) {
        return heads.stream()
                .filter(Iterator::hasNext)
                .map(Iterator::next)
                .collect(Collectors.toList());
    }
}

{'fraction_non_alphanumeric': 0.113273106323836, 'fraction_numerical': 0.003995830437804031, 'mean_word_length': 2.536240786240786, 'pattern_counts': {'":': 0, '<': 49, '<?xml version=': 0, '>': 54, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, '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': '7065138', 'n_tokens_mistral': 1654, 'n_tokens_neox': 1561, 'n_words': 314}