Pith - kiln
kiln/app/src/main/java/com/vgmlr/kiln/KilnMoodInfluence.kt [2.0 kb]
Modified: 00:02:26 153 026 (19 Aug 026)
5 Days Ago
package com.vgmlr.kiln

import java.time.LocalDate
import java.time.temporal.ChronoUnit
import kotlin.math.abs

object KilnMoodInfluence {
    private const val MIN_MOOD_DAYS = 20
    private const val RAMP_DAYS = 28
    private const val MAX_SHIFT_DEG = 30f
    private const val SMOOTH = 2L

    fun weight(records: List<KilnDayRecord>, buttons: KilnButtonSet): Float {
        val n = records.count { it.mood != null || buttons.hasSymptom(it) }
        return ((n - MIN_MOOD_DAYS).toFloat() / RAMP_DAYS).coerceIn(0f, 1f)
    }
    
    private val SENTIMENT = floatArrayOf(-1f, -0.4f, 1f, -0.4f, -1f)
    private fun sentiment(mood: Int): Float = SENTIMENT.getOrElse(mood) { 0f }

    private const val SYMPTOM_SENTIMENT = -0.4f
    
    fun hueShift(
        records: List<KilnDayRecord>,
        buttons: KilnButtonSet,
        starts: List<LocalDate>,
        cycle: Int,
        date: LocalDate
    ): Float {
        val w = weight(records, buttons)
        if (w == 0f || starts.isEmpty()) return 0f
        val phase = phaseOf(date, starts, cycle) ?: return 0f

        var sum = 0f
        var n = 0
        for (r in records) {
            val p = phaseOf(LocalDate.ofEpochDay(r.epochDay), starts, cycle) ?: continue
            if (circularDist(p, phase, cycle) > SMOOTH) continue
            r.mood?.let { sum += sentiment(it); n++ }
            val s = buttons.symptomCount(r)
            if (s > 0) {
                sum += SYMPTOM_SENTIMENT * s
                n += s
            }
        }
        if (n == 0) return 0f

        val avg = sum / n
        val confidence = n / (n + 3f)
        return avg * MAX_SHIFT_DEG * w * confidence
    }

    private fun phaseOf(date: LocalDate, starts: List<LocalDate>, cycle: Int): Long? {
        val last = starts.lastOrNull { !it.isAfter(date) } ?: return null
        return Math.floorMod(ChronoUnit.DAYS.between(last, date), cycle.toLong())
    }

    private fun circularDist(a: Long, b: Long, cycle: Int): Long {
        val d = abs(a - b)
        return minOf(d, cycle - d)
    }
}
Updates
Kerf - Android 157.026
Kiln - Android 157.026
Wedge - Android 156.026
Whittle - Linux 155.026
Grit - Firefox/Chrome 154.026

Menu
Calendar
Project Tin (024/029)
Miter
RSS Feed
User Avatar
@vgmlr
=SUM(parts)
0.00258
257,108 (+362)