razionale.php

<?php
declare(strict_types=1);

class razionale
{
	private int $num;
	private int $den;

	public function __construct(int $num = 0, int $den = 1)
	{
		$this->num = $num;
		$this->den = $den;
	}

	public function sign(): int
	{
		return $this->num <=> 0;
	}

	public function addfrac(razionale $x, razionale $y): void
	{
		$somma = new razionale();
		if ($x->num == 0) {
			$this->num = $y->num;
			$this->den = $y->den;
		} else if ($y->num() == 0) {
			$this->num = $x->num;
			$this->den = $x->den;
		} else {
			$somma->num = $x->num * $y->den + $x->den * $y->num;
			$somma->den = $x->den * $y->den;
			$somma->normalize();
			$this->num = $somma->num;
			$this->den = $somma->den;
		}
	}

	public function num(): int
	{
		return $this->num;
	}

	public function normalize(): self
	{
		$s = $this->sign_int($this->den);
		if ($s == 0) printf("Zero denominator.");
		else if ($s < 0) {
			$this->den = -$this->den;
			$this->num = -$this->num;
		}
		$g = $this->gcd($this->num, $this->den);
		if ($g != 1) {
			$this->num /= $g;
			$this->den /= $g;
		}
		return $this;
	}

	public function sign_int(int $i): int
	{
		return $i <=> 0;
	}

	public function gcd(int $x, int $y): int
	{
		$a = abs($x);
		$b = abs($y);
		if ($b > $a) {
			$tmp = $a;
			$a = $b;
			$b = $tmp;
		}
		for (; ;) {
			if ($b == 0) return $a;
			else if ($b == 1) return $b;
			else {
				$tmp = $b;
				$b = $a % $b;
				$a = $tmp;
			}
		}
	}

	public function mulfrac(razionale $x, razionale $y): void
	{
		$prodotto = new razionale();
		if ($x->num == 0 || $y->num == 0) {
			$this->num = 0;
			$this->den = 1;
		} else if ($x->value() == 1) {
			$this->num = $y->num;
			$this->den = $y->den;
		} else if ($y->value() == 1) {
			$this->num = $x->num;
			$this->den = $x->den;
		} else if ($x->value() == -1) {
			$this->num = -$y->num;
			$this->den = $y->den;
		} else if ($y->value() == -1) {
			$this->num = -$x->num;
			$this->den = $x->den;
		} else {
			$prodotto->num = $x->num * $y->num;
			$prodotto->den = $x->den * $y->den;
			$prodotto->normalize();
			$this->num = $prodotto->num;
			$this->den = $prodotto->den;
		}
	}

	public function value(): float
	{
		return $this->den != 0 ? $this->num / $this->den : NAN;
	}

	public function divfrac(razionale $x, razionale $y): void
	{
		$quoziente = new razionale();
		if ($x->num == 0) {
			$this->num = 0;
			$this->den = 1;
		} else if ($y->num == 0) {
			printf("Division by zero");
		} else {
			// echo "\n<br />divido " . $x->fractoa() . " per " . $y->fractoa();
			$quoziente->num = $x->num * $y->den;
			$quoziente->den = $x->den * $y->num;
			$quoziente->normalize();
			$this->num = $quoziente->num;
			$this->den = $quoziente->den;
			// echo " e ottengo " . $this->fractoa() . "<br />\n";

		}
	}

	public function negatefrac(): void
	{
		$this->num = -$this->num;
	}

	public function invertfrac(): void
	{
		$tmp = $this->num;
		$this->num = $this->den;
		$this->den = $tmp;
		$s = $this->sign_int($this->den);
		if ($s == 0) exit(1);
		else if ($s < 0) {
			$this->den = -$this->den;
			$this->num = -$this->num;
		}
	}

	public function comparefrac(razionale $x, razionale $y): int
	{
		$this->subfrac($x, $y);
		return $this->sign_int($this->num);
	}

	public function subfrac(razionale $x, razionale $y): void
	{
		$differenza = new razionale();
		if ($x->num == 0) {
			$this->num = -$y->num;
			$this->den = $y->den;
		} else if ($y->num == 0) {
			$this->num = $x->num;
			$this->den = $x->den;
		} else {
			$differenza->num = $x->num * $y->den - $x->den * $y->num;
			$differenza->den = $x->den * $y->den;
			$differenza->normalize();
			$this->num = $differenza->num;
			$this->den = $differenza->den;
		}
	}

	public function scanfrac(string $x): void
	{
		// Se c'e' uno slash e' espresso direttamente come frazione
		if (empty(trim($x))) {
			$this->num = 0;
			$this->den = 1;
		} else if (strchr($x, '/')) {
			$num_den = explode('/', $x, strlen($x));
			$this->num = (int)trim($num_den[0]);
			$this->den = (int)trim($num_den[1]);
		} else if (strchr($x, '.')) { // e' espresso come decimale
			// facciamo fare il lavoro al PHP
			$float = (float)$x;
			// quante cifre dopo la virgola?
			$decimali = strlen(strchr($x, '.')) - 1;
			$this->den = (int)pow(10, $decimali);
			$this->num = (int)round($float * $this->den);
		} else { // e' un intero
			$this->num = (int)$x;
			$this->den = 1;
		}
	}

	public function fractoa(): string
	{
		if ($this->den == 1)
			$str = sprintf("%d", $this->num);
		else
			$str = sprintf("%d/%d", $this->num, $this->den);
		return $str;
	}

	public function den(): int
	{
		return $this->den;
	}

	public function set(int $num, int $den): void
	{
		$this->num = $num;
		$this->den = $den;
	}
}