<commit_before><?php

namespace Vector\Control;

use ReflectionFunction;
use ReflectionParameter;
use Vector\Core\Exception\ElementNotFoundException;
use Vector\Core\Exception\IncompletePatternMatchException;
use Vector\Core\Module;
use Vector\Lib\Arrays;

/**
 * @method static mixed match(...$args)
 */
abstract class Pattern extends Module
{
    /**
     * Get type OR class for params, as well as re-mapping inconsistencies
     * @param $param
     * @return string
     */
    protected static function __getType($param)
    {
        $type = is_object($param)
            ? get_class($param)
            : gettype($param);

        return $type === 'integer' ? 'int' : $type;
    }

    /**
     * Pattern Matching. Use switch-case for explicit values, this for everything else.
     * @param array $patterns
     * @return mixed
     */
    protected static function __match(array $patterns)
    {
        return function (...$args) use ($patterns) {
            $parameterTypes = array_map(self::getType(), $args);

            try {
                $keysToValues = Arrays::zip(array_keys($patterns), array_values($patterns));

                list($key, $matchingPattern) = Arrays::first(
                    Pattern::patternApplies($parameterTypes, $args),
                    $keysToValues
                );
            } catch (ElementNotFoundException $e) {
                throw new IncompletePatternMatchException(
                    'Incomplete pattern match expression. (missing ' . implode(', ', $parameterTypes) . ')'
                );
            }

            list($hasExtractable, $unwrappedArgs) = self::unwrapArgs($args);

            $value = $matchingPattern(...$args);
            $isCallable = is_callable($value);

            /**
             * Extractable requires a callback to feed args into.
             */
            if ($hasExtractable && $isCallable) {
                return $matchingPattern(...$args)(...$unwrappedArgs);
            }

            /**
             * No extractable or callable so we can just return the value directly.
             */
            return $value;
        };
    }

    /**
     * Extracts args from Extractable values
     * @param array $args
     * @return array
     */
    protected static function unwrapArgs(array $args) : array
    {
        $hasExtractable = false;

        $unwrappedArgs = self::flatten(array_map(function ($arg) use (&$hasExtractable) {
            if (method_exists($arg, 'extract')) {
                $hasExtractable = true;
                return $arg->extract();
            }

            return $arg;
        }, $args));

        return [$hasExtractable, $unwrappedArgs];
    }

    /**
     * @param array $parameterTypes
     * @param array $args
     * @param array $pattern
     * @return bool
     * @throws \ReflectionException
     */
    protected static function __patternApplies(array $parameterTypes, array $args, array $pattern) : bool
    {
        list($key, $pattern) = $pattern;

        $reflected = new ReflectionFunction($pattern);

        $patternParameterTypes = array_map(function (ReflectionParameter $parameter) {
            $class = $parameter->getClass();
            if ($class) {
                return $class->getName();
            }

            return (string) $parameter->getType();
        }, $reflected->getParameters());

        /**
         * Check count/type of params
         */
        return count($patternParameterTypes) === 0
            || (
                count($parameterTypes) === count($patternParameterTypes)
                && $parameterTypes === $patternParameterTypes
            );
    }

    /**
     * @param array $array<commit_msg>fix deprecated toString on reflectionparameter.<commit_after><?php

namespace Vector\Control;

use ReflectionFunction;
use ReflectionParameter;
use Vector\Core\Exception\ElementNotFoundException;
use Vector\Core\Exception\IncompletePatternMatchException;
use Vector\Core\Module;
use Vector\Lib\Arrays;

/**
 * @method static mixed match(...$args)
 */
abstract class Pattern extends Module
{
    /**
     * Get type OR class for params, as well as re-mapping inconsistencies
     * @param $param
     * @return string
     */
    protected static function __getType($param)
    {
        $type = is_object($param)
            ? get_class($param)
            : gettype($param);

        return $type === 'integer' ? 'int' : $type;
    }

    /**
     * Pattern Matching. Use switch-case for explicit values, this for everything else.
     * @param array $patterns
     * @return mixed
     */
    protected static function __match(array $patterns)
    {
        return function (...$args) use ($patterns) {
            $parameterTypes = array_map(self::getType(), $args);

            try {
                $keysToValues = Arrays::zip(array_keys($patterns), array_values($patterns));

                list($key, $matchingPattern) = Arrays::first(
                    Pattern::patternApplies($parameterTypes, $args),
                    $keysToValues
                );
            } catch (ElementNotFoundException $e) {
                throw new IncompletePatternMatchException(
                    'Incomplete pattern match expression. (missing ' . implode(', ', $parameterTypes) . ')'
                );
            }

            list($hasExtractable, $unwrappedArgs) = self::unwrapArgs($args);

            $value = $matchingPattern(...$args);
            $isCallable = is_callable($value);

            /**
             * Extractable requires a callback to feed args into.
             */
            if ($hasExtractable && $isCallable) {
                return $matchingPattern(...$args)(...$unwrappedArgs);
            }

            /**
             * No extractable or callable so we can just return the value directly.
             */
            return $value;
        };
    }

    /**
     * Extracts args from Extractable values
     * @param array $args
     * @return array
     */
    protected static function unwrapArgs(array $args) : array
    {
        $hasExtractable = false;

        $unwrappedArgs = self::flatten(array_map(function ($arg) use (&$hasExtractable) {
            if (method_exists($arg, 'extract')) {
                $hasExtractable = true;
                return $arg->extract();
            }

            return $arg;
        }, $args));

        return [$hasExtractable, $unwrappedArgs];
    }

    /**
     * @param array $parameterTypes
     * @param array $args
     * @param array $pattern
     * @return bool
     * @throws \ReflectionException
     */
    protected static function __patternApplies(array $parameterTypes, array $args, array $pattern) : bool
    {
        list($key, $pattern) = $pattern;

        $reflected = new ReflectionFunction($pattern);

        $patternParameterTypes = array_map(function (ReflectionParameter $parameter) {
            $class = $parameter->getClass();
            if ($class) {
                return $class->getName();
            }

            return (string) ($parameter->getType() ? $parameter->getType()->getName() : null);
        }, $reflected->getParameters());

        /**
         * Check count/type of params
         */
        return count($patternParameterTypes) === 0
            || (
                count($parameterTypes) === count($patternParameterTypes)
                && $parameterTypes === $patternParameterTypes
            );
    }

    /**
     * @param array $array
{'fraction_non_alphanumeric': 0.1201923076923077, 'fraction_numerical': 0.0002670940170940171, 'mean_word_length': 1.9437893081761006, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1038181', 'n_tokens_mistral': 1964, 'n_tokens_neox': 1894, 'n_words': 467}