<commit_before>/**
 * Copyright 2015 <NAME>, All rights reserved.
 *
 * Project Info:
 * https://github.com/Petikoch/feedback_control_bookexamples_in_java
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package ch.petikoch.examples.feedbackControlInJava.ch13;

import ch.petikoch.examples.feedbackControlInJava.simulationFramework.Component;
import ch.petikoch.examples.feedbackControlInJava.simulationFramework.datatypes.ZeroOrOne;
import com.google.common.base.Preconditions;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import net.jcip.annotations.NotThreadSafe;

import java.util.Map;

/**
 * A java port of the Cache python class from
 * https://github.com/oreillymedia/feedback_control_for_computer_systems/blob/master/ch13-cache.py
 */
@NotThreadSafe
public class Cache implements Component<Integer, Double> {

    private final DemandFunction<Integer> demandFunction;
    private final BiMap<Integer, Long> items2LastAccessTimeMap;

    private long time = 0;
    private int requestedCacheSize;

    public Cache(int initialCacheSize, DemandFunction<Integer> demandFunction) {
        Preconditions.checkArgument(initialCacheSize >= 0);
        Preconditions.checkNotNull(demandFunction);

        this.requestedCacheSize = initialCacheSize;
        this.demandFunction = demandFunction;
        this.items2LastAccessTimeMap = HashBiMap.create(initialCacheSize);
    }

    /**
     * @param newCacheSize input from the controller which tells the cache how large it should be
     * @return the hitrate: a zero if it wasn't a hit, a one if it was a hit
     */
    @Override
    public Double work(Integer newCacheSize) {
        return simulateCacheAccess(newCacheSize).doubleValue();
    }

    protected final ZeroOrOne simulateCacheAccess(Integer newCacheSize) {
        time++;
        requestedCacheSize = newCacheSize > 0 ? newCacheSize : -newCacheSize;
        Integer requestedItemAtCurrentTime = demandFunction.demand(time);
        if (items2LastAccessTimeMap.containsKey(requestedItemAtCurrentTime)) {
            items2LastAccessTimeMap.put(requestedItemAtCurrentTime, time);
            return ZeroOrOne.ONE; // hit :-)
        } else {
            if (items2LastAccessTimeMap.size() >= requestedCacheSize) {
                shrinkCacheByRemovingOldestEntries();
            }
            items2LastAccessTimeMap.put(requestedItemAtCurrentTime, time);
            return ZeroOrOne.ZERO; // no hit :-(
        }
    }

    private void shrinkCacheByRemovingOldestEntries() {
        int numberOfElementsToDelete = 1 + items2LastAccessTimeMap.size() - requestedCacheSize;
        Map<Long, Integer> lastAccessTimeCache2Item = items2LastAccessTimeMap.inverse();
        lastAccessTimeCache2Item.keySet().stream()
                .sorted()<commit_msg>Bugfix (a bug in the original phyton code): We need to shrink the cache in any case, not just in case of a miss.<commit_after>/**
 * Copyright 2015 <NAME>, All rights reserved.
 *
 * Project Info:
 * https://github.com/Petikoch/feedback_control_bookexamples_in_java
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package ch.petikoch.examples.feedbackControlInJava.ch13;

import ch.petikoch.examples.feedbackControlInJava.simulationFramework.Component;
import ch.petikoch.examples.feedbackControlInJava.simulationFramework.datatypes.ZeroOrOne;
import com.google.common.base.Preconditions;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import net.jcip.annotations.NotThreadSafe;

import java.util.Map;

/**
 * A java port of the Cache python class from
 * https://github.com/oreillymedia/feedback_control_for_computer_systems/blob/master/ch13-cache.py
 */
@NotThreadSafe
public class Cache implements Component<Integer, Double> {

    private final DemandFunction<Integer> demandFunction;
    private final BiMap<Integer, Long> items2LastAccessTimeMap;

    private long time = 0;

    public Cache(int initialCacheSize, DemandFunction<Integer> demandFunction) {
        Preconditions.checkArgument(initialCacheSize >= 0);
        Preconditions.checkNotNull(demandFunction);

        this.demandFunction = demandFunction;
        this.items2LastAccessTimeMap = HashBiMap.create(initialCacheSize);
    }

    /**
     * @param newCacheSize input from the controller which tells the cache how large it should be
     * @return the hitrate: a zero if it wasn't a hit, a one if it was a hit
     */
    @Override
    public Double work(Integer newCacheSize) {
        return simulateCacheAccess(newCacheSize).doubleValue();
    }

    protected final ZeroOrOne simulateCacheAccess(Integer newCacheSize) {
        time++;

        Preconditions.checkArgument(newCacheSize >= 0, "A negative cache size doesn't make sense.");

        if (items2LastAccessTimeMap.size() >= newCacheSize) {
            shrinkCacheByRemovingOldestEntries(newCacheSize);
        }

        Integer requestedItemAtCurrentTime = demandFunction.demand(time);
        if (items2LastAccessTimeMap.containsKey(requestedItemAtCurrentTime)) {
            items2LastAccessTimeMap.put(requestedItemAtCurrentTime, time);
            return ZeroOrOne.ONE; // hit :-)
        } else {
            items2LastAccessTimeMap.put(requestedItemAtCurrentTime, time);
            return ZeroOrOne.ZERO; // no hit :-(
        }
    }

    private void shrinkCacheByRemovingOldestEntries(int newCacheSize) {
        int numberOfElementsToDelete = 1 + items2LastAccessTimeMap.size() - newCacheSize;
        Map<Long, Integer> lastAccessTimeCache2Item = items2LastAccessTimeMap.inverse();
        lastAccessTimeCache2Item.keySet().stream()
                .sorted()
{'fraction_non_alphanumeric': 0.08294654888456356, 'fraction_numerical': 0.007785596646204522, 'mean_word_length': 3.7241867043847243, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 21, 'https://': 4, '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': '2563140', 'n_tokens_mistral': 1854, 'n_tokens_neox': 1774, 'n_words': 494}