<?php

namespace MathPHP\Tests\Functions;

use MathPHP\Exception;
use MathPHP\Functions\ArbitraryInteger;
use MathPHP\Number;

class ArbitraryIntegerTest extends \PHPUnit\Framework\TestCase
{
    /**
     * @test         ackermann
     * @dataProvider dataProviderForAckermann
     * @param        int $m
     * @param        int $n
     * @param        string $expected
     * @throws       \Exception
     */
    public function testAckermann(int $m, int $n, string $expected)
    {
        // When
        $int = ArbitraryInteger::ackermann($m, $n);

        // Then
        $this->assertEquals($expected, (string) $int);
    }

    public function dataProviderForAckermann(): array
    {
        return [
            [0, 0, '1'],
            [0, 1, '2'],
            [1, 0, '2'],
            [1, 1, '3'],
            [2, 0, '3'],
            [0, 2, '3'],
            [2, 1, '5'],
            [1, 2, '4'],
            [2, 2, '7'],
            [2, 3, '9'],
            [3, 2, '29'],
            [3, 6, '509'],
            [0, 7, '8'],
            [1, 100, '102'],
            [2, 200, '403'],
            [3, 4, '125'],
            [4, 1, '65533'],
            [5, 0, '65533'],

            // Manual testing only - Takes a long time to convert the base 256 to base 10
            // [4, 2, '[encoded data, 19729 characters]'],
        ];
    }

    /**
     * @test   rand produces an appropriately sized random number
     * @throws \Exception
     */
    public function testRand()
    {
        foreach (\range(1, 10) as $_) {
            // When
            $number = ArbitraryInteger::rand(1);

            // Then
            $this->assertInstanceOf(Number\ArbitraryInteger::class, $number);
            $this->assertTrue($number->lessThan(65536));
        }
    }

    /**
     * @test   ackermann throws an exception when an object is provided
     * @throws \Exception
     */
    public function testIncorrectTypeExceptionPrepareParameter()
    {
        // Given
        $m = new \stdClass();
        $n = $m;

        // Then
        $this->expectException(Exception\IncorrectTypeException::class);

        // When
        $int = ArbitraryInteger::ackermann($m, $n);
    }

    /**
     * @test   rand throws an exception when given an int less than 1
     * @throws \Exception
     */
    public function testBadRandParameter()
    {
        // Given
        $number = 0;

        // Then
        $this->expectException(Exception\BadParameterException::class);

        // When
        $int = ArbitraryInteger::rand($number);
    }
}
