<commit_before><?php
/**
 * @link   https://github.com/zionsg/MagicSquare for repository
 * @author <NAME> <<EMAIL>>
 * @since  2016-06-19T20:00+08:00
 */

namespace IntZone\MathZ;

/**
 * Magic square constructor
 *
 * @todo Only order 4m supported for now
 */
class MagicSquare
{
    /**
     * Supported orders of magic squares
     *
     * @var array
     */
    protected $orders = [];

    /**
     * Constructor
     *
     * Setup callbacks for supported orders of magic squares.
     */
    public function __construct()
    {
        $this->orders = [
            /*
            'odd' => [
                'test' => function ($n) { return (1 === $n % 2); },
                'computeWidth' => function ($cellCount) {
                    $sqrt = (int) ceil(sqrt($cellCount));
                    return $sqrt + (0 === $sqrt % 2 ? 1 : 0);
                },
                'generator' => 'generateOdd',
            ],
            */

            '4m' => [
                'test' => function ($n) { return (0 === $n % 4); },
                'computeWidth' => function ($cellCount) { return (int) (ceil(sqrt($cellCount) / 4) * 4); },
                'generator' => 'generate4m',
            ],
        ];
    }

    /**
     * Compute minimum width of magic square needed to contain a number of cells
     *
     * A cell may contain a character, number, word, etc. A 4x4 magic square consists of 16 cells.
     * This goes thru all the supported orders, compute the respective widths and finds the smallest.
     *
     * @param  int $cellCount
     * @throws InvalidArgumentException if $cellCount is not a positive integer
     * @return int Returns 0 if width cannot be computed
     */
    public function computeWidth($cellCount)
    {
        $this->assertPositiveInteger($cellCount);

        $widths = [];
        foreach ($this->orders as $order) {
            $widthFn = $order['computeWidth'];
            if (is_callable($widthFn)) {
                $widths[] = $widthFn($cellCount);
            }
        }

        return ($widths ? min($widths) : 0);
    }

    /**
     * Create n x n magic square with numbers 1 to n
     *
     * @param  int $n
     * @throws InvalidArgumentException if n is not a positive integer
     * @throws DomainException if unable to generate for n
     * @return array [[<row 1 column 1>, <row 1 column 2>], [<row 2 column 1>, <row 2 column 2]]
     */
    public function generate($n)
    {
        $this->assertPositiveInteger($n);

        foreach ($this->orders as $order) {
            $testFn = $order['test'];
            if (is_callable($testFn) && $testFn($n)) {
                $generator = $order['generator'];
                return $this->$generator($n);
            }
        }

        throw new DomainException("Unable to generate {$n} x {$n} magic square");
    }

    /**
     * Render magic square as HTML table
     *
     * @param  array  $magicSquare     Result of generate()
     * @param  bool   $useDefaultStyle Whether to use default CSS styling for table
     * @param  string $tableClass      Optional CSS class for HTML table
     * @return string
     */
    public function render(array $magicSquare, $useDefaultStyle = true, $tableClass = '')
    {
        $n = count($magicSquare);

        $tableStyle = $useDefaultStyle ? 'border-collapse:collapse; border-spacing:0;' : '';
        $tdStyle = $useDefaultStyle
                 ? 'border:1px solid black; font-family:monospace; text-align:center; vertical-align:middle;'
                 : '';

        $output = sprintf('<table class="%s" style="%s">', $tableClass, $tableStyle);
        for ($row = 0; $row < $n; $row++) {
            $output .= '<tr>';
            for ($col = 0; $col < $n; $col++) {
                $output .= sprintf(
                    '<td style="%s">%s</td>',
                    $tdStyle,
                    $magicSquare[$row][$col]
                );
            }
            $output .= '</tr>';
        }
        $output .= '</table>';

        return $output;
    }

    /**
     * Assert that a variable is a positive integer
     *
     * @param  mixed $var
     * @throws InvalidArgumentException if assertion fails
     * @return bool
     */
    protected function assertPositiveInteger($var)
    {
        $result = (is_int($var) && $var > 0);

        if (!$result) {
            throw new InvalidArgumentException("{$var} is not a positive integer");
        }

        return $result;
    }

    /**
     * Create n x n magic square where n = 4m, m being a positive integer
     *
     * @param  int $n
     * @throws InvalidArgumentException if n is not a multiple of 4
     * @return array [[<row 1 column 1>, <row 1 column 2>], [<row 2 column 1>, <row 2 column 2]]
     */
    protected function generate4m($n)<commit_msg>Specify namespaces used for exception classes<commit_after><?php
/**
 * @link   https://github.com/zionsg/MagicSquare for repository
 * @author <NAME> <<EMAIL>>
 * @since  2016-06-19T20:00+08:00
 */

namespace IntZone\MathZ;

use DomainException;
use InvalidArgumentException;

/**
 * Magic square constructor
 *
 * @todo Only order 4m supported for now
 */
class MagicSquare
{
    /**
     * Supported orders of magic squares
     *
     * @var array
     */
    protected $orders = [];

    /**
     * Constructor
     *
     * Setup callbacks for supported orders of magic squares.
     */
    public function __construct()
    {
        $this->orders = [
            /*
            'odd' => [
                'test' => function ($n) { return (1 === $n % 2); },
                'computeWidth' => function ($cellCount) {
                    $sqrt = (int) ceil(sqrt($cellCount));
                    return $sqrt + (0 === $sqrt % 2 ? 1 : 0);
                },
                'generator' => 'generateOdd',
            ],
            */

            '4m' => [
                'test' => function ($n) { return (0 === $n % 4); },
                'computeWidth' => function ($cellCount) { return (int) (ceil(sqrt($cellCount) / 4) * 4); },
                'generator' => 'generate4m',
            ],
        ];
    }

    /**
     * Compute minimum width of magic square needed to contain a number of cells
     *
     * A cell may contain a character, number, word, etc. A 4x4 magic square consists of 16 cells.
     * This goes thru all the supported orders, compute the respective widths and finds the smallest.
     *
     * @param  int $cellCount
     * @throws InvalidArgumentException if $cellCount is not a positive integer
     * @return int Returns 0 if width cannot be computed
     */
    public function computeWidth($cellCount)
    {
        $this->assertPositiveInteger($cellCount);

        $widths = [];
        foreach ($this->orders as $order) {
            $widthFn = $order['computeWidth'];
            if (is_callable($widthFn)) {
                $widths[] = $widthFn($cellCount);
            }
        }

        return ($widths ? min($widths) : 0);
    }

    /**
     * Create n x n magic square with numbers 1 to n
     *
     * @param  int $n
     * @throws InvalidArgumentException if n is not a positive integer
     * @throws DomainException if unable to generate for n
     * @return array [[<row 1 column 1>, <row 1 column 2>], [<row 2 column 1>, <row 2 column 2]]
     */
    public function generate($n)
    {
        $this->assertPositiveInteger($n);

        foreach ($this->orders as $order) {
            $testFn = $order['test'];
            if (is_callable($testFn) && $testFn($n)) {
                $generator = $order['generator'];
                return $this->$generator($n);
            }
        }

        throw new DomainException("Unable to generate {$n} x {$n} magic square");
    }

    /**
     * Render magic square as HTML table
     *
     * @param  array  $magicSquare     Result of generate()
     * @param  bool   $useDefaultStyle Whether to use default CSS styling for table
     * @param  string $tableClass      Optional CSS class for HTML table
     * @return string
     */
    public function render(array $magicSquare, $useDefaultStyle = true, $tableClass = '')
    {
        $n = count($magicSquare);

        $tableStyle = $useDefaultStyle ? 'border-collapse:collapse; border-spacing:0;' : '';
        $tdStyle = $useDefaultStyle
                 ? 'border:1px solid black; font-family:monospace; text-align:center; vertical-align:middle;'
                 : '';

        $output = sprintf('<table class="%s" style="%s">', $tableClass, $tableStyle);
        for ($row = 0; $row < $n; $row++) {
            $output .= '<tr>';
            for ($col = 0; $col < $n; $col++) {
                $output .= sprintf(
                    '<td style="%s">%s</td>',
                    $tdStyle,
                    $magicSquare[$row][$col]
                );
            }
            $output .= '</tr>';
        }
        $output .= '</table>';

        return $output;
    }

    /**
     * Assert that a variable is a positive integer
     *
     * @param  mixed $var
     * @throws InvalidArgumentException if assertion fails
     * @return bool
     */
    protected function assertPositiveInteger($var)
    {
        $result = (is_int($var) && $var > 0);

        if (!$result) {
            throw new InvalidArgumentException("{$var} is not a positive integer");
        }

        return $result;
    }

    /**
     * Create n x n magic square where n = 4m, m being a positive integer
     *
     * @param  int $n
     * @throws InvalidArgumentException if n is not a positive integer
     * @throws InvalidArgumentException if n is not a multiple of 4
     * @return array [[<row 1 column 1>, <row 1 column 2>], [<row 2 column 1>, <row 2 column 2]]
     */
    protected function generate4m($n)
{'fraction_non_alphanumeric': 0.13297817846532117, 'fraction_numerical': 0.012293822354266981, 'mean_word_length': 1.846064139941691, 'pattern_counts': {'":': 0, '<': 43, '<?xml version=': 0, '>': 63, 'https://': 2, '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': '1624913', 'n_tokens_mistral': 2886, 'n_tokens_neox': 2687, 'n_words': 859}