File indexing completed on 2024-05-05 17:33:22

0001 /*
0002  *   SPDX-FileCopyrightText: 2011 Jonathan Thomas <echidnaman@kubuntu.org>
0003  *
0004  *   SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005  */
0006 
0007 #include "Rating.h"
0008 #include "libdiscover_debug.h"
0009 #include <QStringList>
0010 #include <qmath.h>
0011 
0012 inline double fastPow(double a, double b)
0013 {
0014     union {
0015         double d;
0016         int x[2];
0017     } u = {a};
0018 
0019 #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
0020     u.x[1] = (int)(b * (u.x[1] - 1072632447) + 1072632447);
0021     u.x[0] = 0;
0022 #else
0023     u.x[1] = 0;
0024     u.x[0] = (int)(b * (u.x[1] - 1072632447) + 1072632447);
0025 #endif
0026 
0027     return u.d;
0028 }
0029 
0030 // Converted from a Ruby example, returns an inverse normal distribution
0031 double pnormaldist(double qn)
0032 {
0033     double b[] = {1.570796288,
0034                   0.03706987906,
0035                   -0.8364353589e-3,
0036                   -0.2250947176e-3,
0037                   0.6841218299e-5,
0038                   0.5824238515e-5,
0039                   -0.104527497e-5,
0040                   0.8360937017e-7,
0041                   -0.3231081277e-8,
0042                   0.3657763036e-10,
0043                   0.6936233982e-12};
0044 
0045     if (qn < 0.0 || 1.0 < qn)
0046         return 0.0;
0047 
0048     if (qn == 0.5)
0049         return 0.0;
0050 
0051     double w1 = qn;
0052     if (qn > 0.5)
0053         w1 = 1.0 - w1;
0054     double w3 = -qLn(4.0 * w1 * (1.0 - w1));
0055     w1 = b[0];
0056 
0057     for (int i = 1; i < 11; i++)
0058         w1 += b[i] * fastPow(w3, i);
0059 
0060     if (qn > 0.5)
0061         return qSqrt(w1 * w3);
0062     return -qSqrt(w1 * w3);
0063 }
0064 
0065 double wilson_score(int pos, int n, double power = 0.2)
0066 {
0067     if (n == 0)
0068         return 0;
0069 
0070     double z = pnormaldist(1 - power / 2);
0071     double phat = 1.0 * pos / n;
0072     return (phat + z * z / (2 * n) - z * qSqrt((phat * (1 - phat) + z * z / (4 * n)) / n)) / (1 + z * z / n);
0073 }
0074 
0075 double dampenedRating(int ratings[6], double power = 0.1)
0076 {
0077     int tot_ratings = 0;
0078     for (int i = 0; i < 6; ++i)
0079         tot_ratings = ratings[i] + tot_ratings;
0080 
0081     double sum_scores = 0.0;
0082     for (int i = 0; i < 6; i++) {
0083         const int rating = ratings[i];
0084         double ws = wilson_score(rating, tot_ratings, power);
0085         sum_scores = sum_scores + float((i + 1) - 3) * ws;
0086     }
0087 
0088     return sum_scores + 3;
0089 }
0090 
0091 Rating::Rating(const QString &packageName, quint64 ratingCount, int data[6])
0092     : m_packageName(packageName)
0093     , m_ratingCount(ratingCount)
0094     // TODO consider storing data[] and present in UI
0095     , m_rating(((data[1] //
0096                  + (data[2] * 2) //
0097                  + (data[3] * 3) //
0098                  + (data[4] * 4) //
0099                  + (data[5] * 5)) //
0100                 * 2)
0101                / qMax<float>(1, ratingCount))
0102     , m_ratingPoints(0)
0103     , m_sortableRating(0)
0104 {
0105     int spread[6];
0106     for (int i = 0; i < 6; ++i) {
0107         int points = data[i];
0108         m_ratingPoints += (i + 1) * points;
0109         spread[i] = points;
0110     }
0111 
0112     m_sortableRating = dampenedRating(spread) * 2;
0113 }
0114 
0115 Rating::Rating(const QString &packageName, quint64 ratingCount, int rating)
0116     : m_packageName(packageName)
0117     , m_ratingCount(ratingCount)
0118     , m_rating(rating)
0119     , m_ratingPoints(rating)
0120     , m_sortableRating(rating)
0121 {
0122 }
0123 
0124 Rating::~Rating() = default;
0125 
0126 QString Rating::packageName() const
0127 {
0128     return m_packageName;
0129 }
0130 
0131 quint64 Rating::ratingCount() const
0132 {
0133     return m_ratingCount;
0134 }
0135 
0136 float Rating::rating() const
0137 {
0138     return m_rating;
0139 }
0140 
0141 int Rating::ratingPoints() const
0142 {
0143     return m_ratingPoints;
0144 }
0145 
0146 double Rating::sortableRating() const
0147 {
0148     return m_sortableRating;
0149 }