Convert ScoreList to Kotlin

pull/699/head
Alinson S. Xavier 5 years ago
parent 9bc0f44777
commit c023711d16

@ -83,12 +83,12 @@ data class Habit(
if (from.isNewerThan(to)) from = to if (from.isNewerThan(to)) from = to
scores.recompute( scores.recompute(
this.frequency, frequency = frequency,
this.isNumerical, isNumerical = isNumerical,
this.targetValue, targetValue = targetValue,
this.computedEntries, computedEntries = computedEntries,
from, from = from,
to to = to,
) )
} }

@ -25,12 +25,6 @@ data class Score(
val value: Double, val value: Double,
) { ) {
fun compareNewer(other: Score): Int {
return this.timestamp.compareTo(other.timestamp)
}
fun compareOlder(other: Score) = -compareNewer(other)
companion object { companion object {
/** /**
* Given the frequency of the habit, the previous score, and the value of * Given the frequency of the habit, the previous score, and the value of
@ -39,11 +33,6 @@ data class Score(
* The frequency of the habit is the number of repetitions divided by the * The frequency of the habit is the number of repetitions divided by the
* length of the interval. For example, a habit that should be repeated 3 * length of the interval. For example, a habit that should be repeated 3
* times in 8 days has frequency 3.0 / 8.0 = 0.375. * times in 8 days has frequency 3.0 / 8.0 = 0.375.
*
* @param frequency the frequency of the habit
* @param previousScore the previous score of the habit
* @param checkmarkValue the value of the current checkmark
* @return the current score
*/ */
@JvmStatic @JvmStatic
fun compute( fun compute(

@ -1,131 +0,0 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.core.models;
import androidx.annotation.*;
import java.util.*;
import static org.isoron.uhabits.core.models.Entry.*;
public class ScoreList
{
private final HashMap<Timestamp, Score> list = new HashMap<>();
/**
* Returns the score for a given day. If the timestamp given happens before the first
* repetition of the habit or after the last computed score, returns a score with value zero.
*/
public final synchronized Score get(Timestamp timestamp)
{
if (list.containsKey(timestamp)) return list.get(timestamp);
return new Score(timestamp, 0);
}
/**
* Returns the list of scores that fall within the given interval.
* <p>
* There is exactly one score per day in the interval. The endpoints of
* the interval are included. The list is ordered by timestamp (decreasing).
* That is, the first score corresponds to the newest timestamp, and the
* last score corresponds to the oldest timestamp.
*
* @param fromTimestamp timestamp of the beginning of the interval.
* @param toTimestamp timestamp of the end of the interval.
* @return the list of scores within the interval.
*/
@NonNull
public List<Score> getByInterval(@NonNull Timestamp fromTimestamp,
@NonNull Timestamp toTimestamp)
{
List<Score> result = new LinkedList<>();
if (fromTimestamp.isNewerThan(toTimestamp)) return result;
Timestamp current = toTimestamp;
while(!current.isOlderThan(fromTimestamp))
{
result.add(get(current));
current = current.minus(1);
}
return result;
}
public void recompute(
Frequency frequency,
boolean isNumerical,
double targetValue,
EntryList computedEntries,
Timestamp from,
Timestamp to)
{
list.clear();
if (computedEntries.getKnown().isEmpty()) return;
if (from.isNewerThan(to)) return;
double rollingSum = 0.0;
int numerator = frequency.getNumerator();
int denominator = frequency.getDenominator();
final double freq = frequency.toDouble();
final Integer[] values = computedEntries
.getByInterval(from, to)
.stream()
.map(Entry::getValue)
.toArray(Integer[]::new);
// For non-daily boolean habits, we double the numerator and the denominator to smooth
// out irregular repetition schedules (for example, weekly habits performed on different
// days of the week)
if (!isNumerical && freq < 1.0)
{
numerator *= 2;
denominator *= 2;
}
double previousValue = 0;
for (int i = 0; i < values.length; i++)
{
int offset = values.length - i - 1;
if (isNumerical)
{
rollingSum += values[offset];
if (offset + denominator < values.length)
{
rollingSum -= values[offset + denominator];
}
double percentageCompleted = Math.min(1, rollingSum / 1000 / targetValue);
previousValue = Score.compute(freq, previousValue, percentageCompleted);
}
else
{
if (values[offset] == YES_MANUAL)
rollingSum += 1.0;
if (offset + denominator < values.length)
if (values[offset + denominator] == YES_MANUAL)
rollingSum -= 1.0;
if (values[offset] != SKIP)
{
double percentageCompleted = Math.min(1, rollingSum / numerator);
previousValue = Score.compute(freq, previousValue, percentageCompleted);
}
}
Timestamp timestamp = from.plus(i);
list.put(timestamp, new Score(timestamp, previousValue));
}
}
}

@ -0,0 +1,114 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.core.models
import org.isoron.uhabits.core.models.Score.Companion.compute
import java.util.*
import kotlin.math.*
class ScoreList {
private val map = HashMap<Timestamp, Score>()
/**
* Returns the score for a given day. If the timestamp given happens before the first
* repetition of the habit or after the last computed score, returns a score with value zero.
*/
operator fun get(timestamp: Timestamp): Score {
return map[timestamp] ?: Score(timestamp, 0.0)
}
/**
* Returns the list of scores that fall within the given interval.
*
* There is exactly one score per day in the interval. The endpoints of the interval are
* included. The list is ordered by timestamp (decreasing). That is, the first score
* corresponds to the newest timestamp, and the last score corresponds to the oldest timestamp.
*/
fun getByInterval(
fromTimestamp: Timestamp,
toTimestamp: Timestamp,
): List<Score> {
val result: MutableList<Score> = ArrayList()
if (fromTimestamp.isNewerThan(toTimestamp)) return result
var current = toTimestamp
while (!current.isOlderThan(fromTimestamp)) {
result.add(get(current))
current = current.minus(1)
}
return result
}
/**
* Recomputes all scores between the provided [from] and [to] timestamps.
*/
fun recompute(
frequency: Frequency,
isNumerical: Boolean,
targetValue: Double,
computedEntries: EntryList,
from: Timestamp,
to: Timestamp,
) {
map.clear()
if (computedEntries.getKnown().isEmpty()) return
if (from.isNewerThan(to)) return
var rollingSum = 0.0
var numerator = frequency.numerator
var denominator = frequency.denominator
val freq = frequency.toDouble()
val values = computedEntries.getByInterval(from, to).map { it.value }.toIntArray()
// For non-daily boolean habits, we double the numerator and the denominator to smooth
// out irregular repetition schedules (for example, weekly habits performed on different
// days of the week)
if (!isNumerical && freq < 1.0) {
numerator *= 2
denominator *= 2
}
var previousValue = 0.0
for (i in values.indices) {
val offset = values.size - i - 1
if (isNumerical) {
rollingSum += values[offset]
if (offset + denominator < values.size) {
rollingSum -= values[offset + denominator]
}
val percentageCompleted = min(1.0, rollingSum / 1000 / targetValue)
previousValue = compute(freq, previousValue, percentageCompleted)
} else {
if (values[offset] == Entry.YES_MANUAL) {
rollingSum += 1.0
}
if (offset + denominator < values.size) {
if (values[offset + denominator] == Entry.YES_MANUAL) {
rollingSum -= 1.0
}
}
if (values[offset] != Entry.SKIP) {
val percentageCompleted = Math.min(1.0, rollingSum / numerator)
previousValue = compute(freq, previousValue, percentageCompleted)
}
}
val timestamp = from.plus(i)
map[timestamp] = Score(timestamp, previousValue)
}
}
}
Loading…
Cancel
Save