[a-zA-Z0-9_-]+):(?P[a-zA-Z0-9_-]+)$/', $matches, PREG_UNMATCHED_AS_NULL)) { return [ 'entity' => $matches['entity'], 'uid' => $matches['uid'] ]; } throw new InvalidArgumentException(sprintf('Invalid address provided: %s', $address)); } /** * Serializes an array into a string. * * @param array $data * @param string $method * @return string */ public static function serialize(array|SerializableObjectInterface $data, string $method): string { if($data instanceof SerializableObjectInterface) { $data = $data->toArray(); } switch(strtolower($method)) { case SerializationMethod::JSON: return json_encode($data); case SerializationMethod::MSGPACK: return msgpack_pack($data); default: Log::warning('net.nosial.federationlib', sprintf('Unknown serialization method: %s, defaulting to msgpack', $method)); return msgpack_pack($data); } } /** * Recursively converts a Throwable into an array representation. * * @param Throwable $throwable * @return array */ public static function throwableToArray(Throwable $throwable): array { $results = [ 'message' => $throwable->getMessage(), 'code' => $throwable->getCode(), 'file' => $throwable->getFile(), 'line' => $throwable->getLine(), 'trace' => $throwable->getTrace(), 'previous' => $throwable->getPrevious() ]; if($results['previous'] instanceof Throwable) { $results['previous'] = self::throwableToArray($results['previous']); } return $results; } /** * Uses the z-score method to detect anomalies in an array of numbers. * The key of the returned array is the index of the number in the original array. * The value of the returned array is the probability of the number being an anomaly. * Negative values are anomalies, positive values are not. * The higher the absolute value, the more likely it is to be an anomaly. * * @param array $data An array of numbers to check for anomalies * @param int $threshold The threshold to use for detecting anomalies * @param bool $filter True to only return anomalies, false to return all values * @return array An array of probabilities */ public static function detectAnomalies(array $data, int $threshold = 2, bool $filter=true): array { $mean = array_sum($data) / count($data); $squares = array_map(static function($x) use ($mean) { return ($x - $mean) ** 2; }, $data); $standard_deviation = sqrt(array_sum($squares) / count($data)); $outliers = []; foreach ($data as $key => $value) { $z_score = ($value - $mean) / $standard_deviation; $probability = exp(-$z_score ** 2 / 2) / (sqrt(2 * M_PI) * $standard_deviation); if($filter) { if ($z_score >= $threshold) { $outliers[$key] = -$probability; } } else { $outliers[$key] = $probability; } } return $outliers; } public static function weightedRandomPick( array $data): string { $totalWeight = array_sum($data); if($totalWeight == 0) { throw new InvalidArgumentException('Total weight cannot be 0'); } // Normalize weights to 0-1 foreach ($data as $item => $weight) { $data[$item] = $weight / $totalWeight; } // Generate a random number between 0 and 1 $rand = mt_rand() / getrandmax(); // Select an item $cumulativeWeight = 0.0; foreach ($data as $item => $weight) { $cumulativeWeight += $weight; if ($rand < $cumulativeWeight) { return $item; } } } }