pragma solidity ^0.5.0; /* @notice: Library used in assessments for clustering, score- and payout computation */ library Math { /* @return: a random number between 0 and a given upper limit (included) based on the last blockhash @dev based on a function by alexvandesande */ function getRandomNumber(uint _seed, uint _max) public view returns(uint) { return(uint(keccak256(abi.encodePacked(blockhash(block.number-1), _seed)))%(_max+1)); } /** @notice Creates clusters of a given radius around all scores. @param _data Array of scores submitted by assessors @param _consentRadius Distance under which two scores are taken to be in consensus. @return finalScore The average of all scores in the largest cluster @return largestClusterSize the number of scores in the largest cluster @dev in case of a two clusters of equal size, the one with a lower score is chosen */ function getFinalScore(int[] memory _data, uint _consentRadius) public pure returns(int finalScore, uint largestClusterSize) { //get largest Cluster and its score int largestClusterScore; for (uint i = 0; i < _data.length; i++) { uint clusterSize = 0; int clusterScore = 0; for (uint j = 0; j < _data.length; j++) { if (abs(_data[i] - _data[j]) <= _consentRadius) { clusterScore += _data[j]; clusterSize++; } } if (clusterSize > largestClusterSize || (clusterSize == largestClusterSize && clusterScore < largestClusterScore)) { largestClusterSize = clusterSize; largestClusterScore = clusterScore; } } finalScore = largestClusterScore/int(largestClusterSize); } /* @param _distance: Absolute difference between the assessor's score and the final score @param _stake: Amount of tokens paid by the assessor to participate @param _consentRadius: Distance under which two scores are taken to be in consensus. @return payout: Sum of reward and the proportion of stake an assessors gets back @return dissenting: Flag whether or not an assessor is part of the majority @dev The reward always is the same amount as the stake. */ function getPayout(uint _distance, uint _stake, uint _consentRadius ) public pure returns(uint payout, bool dissenting) { uint distanceAsPercentageOfRadius = (_distance*10000) / _consentRadius; // if in rewardCluster if (_distance <= _consentRadius) { payout = (_stake * (10000 - distanceAsPercentageOfRadius)) / 10000 + _stake; } else { // cap it to 20000 to prevent underflow //solhint-disable-next-line max-line-length uint distanceAsPercentageOf2RadiusCapped = distanceAsPercentageOfRadius > 20000 ? 20000 : distanceAsPercentageOfRadius; payout = (_stake * (20000 - distanceAsPercentageOf2RadiusCapped)) / 20000; dissenting = true; } } function abs(int x) public pure returns(uint) { return uint(x < 0 ? -x : x); } }