233 lines
4.1 KiB
PHP
233 lines
4.1 KiB
PHP
<?php
|
|
|
|
namespace alveolata\math;
|
|
|
|
|
|
/**
|
|
* modulo operator
|
|
*
|
|
* @author Christian Fraß <frass@greenscale.de>
|
|
*/
|
|
function mod(
|
|
int $number,
|
|
int $modulus
|
|
) : int
|
|
{
|
|
// return (x - (div(x, y) * y));
|
|
if ($modulus <= 0) {
|
|
throw (new \Exception('invalid divisor'));
|
|
}
|
|
else {
|
|
return (
|
|
($number >= 0)
|
|
? ($number%$modulus)
|
|
: (($modulus-((-$number)%$modulus))%$modulus)
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* computes the power of a number in a residue class ring via square and multiply
|
|
*
|
|
* @author Christian Fraß <frass@greenscale.de>
|
|
*/
|
|
function modpow(
|
|
int $base,
|
|
int $exponent,
|
|
int $modulus
|
|
) : int
|
|
{
|
|
if ($exponent === 0) {
|
|
return 1;
|
|
}
|
|
else {
|
|
$a = modpow($base, $exponent >> 1, $modulus);
|
|
$a = mod(($a * $a), $modulus);
|
|
if (($exponent & 1) != 0) {
|
|
$a = mod(($a * $base), $modulus);
|
|
}
|
|
return $a;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @template TypeElement
|
|
* @param \Closure {function<pair<type_element,type_element>,boolean>}
|
|
* @param array $set1 {list<type_element>}
|
|
* @param array $set2 {list<type_element>}
|
|
* @return array {list<type_element>}
|
|
* @author Christian Fraß <frass@greenscale.de>
|
|
*/
|
|
function set_sub/*<type_element>*/(
|
|
\Closure $collation,
|
|
array $set1,
|
|
array $set2
|
|
) : bool
|
|
{
|
|
foreach ($set1 as $element1) {
|
|
$present = false;
|
|
foreach ($set2 as $element2) {
|
|
if ($collation($element1, $element2)) {
|
|
$present = true;
|
|
break;
|
|
}
|
|
}
|
|
if (! $present) {
|
|
return false;
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param \Closure function<pair<type_element,type_element>,boolean>
|
|
* @param array $set1 {list<type_element>}
|
|
* @param array $set2 {list<type_element>}
|
|
* @return array {list<type_element>}
|
|
* @author Christian Fraß <frass@greenscale.de>
|
|
*/
|
|
function set_union/*<type_element>*/(
|
|
\Closure $collation,
|
|
array $set1,
|
|
array $set2
|
|
) : array
|
|
{
|
|
$set3 = [];
|
|
foreach ([$set1, $set2] as $source) {
|
|
foreach ($source as $element) {
|
|
$add = true;
|
|
foreach ($set3 as $element_) {
|
|
if ($collation($element, $element_)) {
|
|
$add = false;
|
|
break;
|
|
}
|
|
}
|
|
if ($add) {
|
|
array_push($set3, $element);
|
|
}
|
|
}
|
|
}
|
|
return $set3;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param \Closure function<pair<type_element,type_element>,boolean>
|
|
* @param array $set1 {list<type_element>}
|
|
* @param array $set2 {list<type_element>}
|
|
* @return array {list<type_element>}
|
|
* @author Christian Fraß <frass@greenscale.de>
|
|
*/
|
|
function set_intersection/*<type_element>*/(
|
|
\Closure $collation,
|
|
array $set1,
|
|
array $set2
|
|
) : array
|
|
{
|
|
$set3 = [];
|
|
foreach ($set1 as $element1) {
|
|
$add = false;
|
|
foreach ($set2 as $element2) {
|
|
if ($collation($element1, $element2)) {
|
|
$add = true;
|
|
break;
|
|
}
|
|
}
|
|
if ($add) {
|
|
array_push($set3, $element1);
|
|
}
|
|
}
|
|
return $set3;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param \Closure function<pair<type_element,type_element>,boolean>
|
|
* @param array $set1 {list<type_element>}
|
|
* @param array $set2 {list<type_element>}
|
|
* @return array {list<type_element>}
|
|
* @author Christian Fraß <frass@greenscale.de>
|
|
*/
|
|
function set_difference/*<type_element>*/(
|
|
\Closure $collation,
|
|
array $set1,
|
|
array $set2
|
|
) : array
|
|
{
|
|
$set3 = [];
|
|
foreach ($set1 as $element1) {
|
|
$add = true;
|
|
foreach ($set2 as $element2) {
|
|
if ($collation($element1, $element2)) {
|
|
$add = false;
|
|
break;
|
|
}
|
|
}
|
|
if ($add) {
|
|
array_push($set3, $element1);
|
|
}
|
|
}
|
|
return $set3;
|
|
}
|
|
|
|
|
|
/**
|
|
* applies the lexicographic order
|
|
*
|
|
* @template type_element
|
|
* @param order Closure {function<type_element,type_element,boolean>}
|
|
* @param array $list1 {list<type_element>}
|
|
* @param array $list2 {list<type_element>}
|
|
* @return boolean
|
|
* @author Christian Fraß <frass@greenscale.de>
|
|
*/
|
|
function order_lexicographic(
|
|
\Closure $order,
|
|
array $list1,
|
|
array $list2
|
|
) : bool
|
|
{
|
|
if (empty($list1)) {
|
|
if (empty($list2)) {
|
|
return true;
|
|
}
|
|
else {
|
|
return true;
|
|
}
|
|
}
|
|
else {
|
|
if (empty($list2)) {
|
|
return false;
|
|
}
|
|
else {
|
|
$le = $order($list1[0], $list2[0]);
|
|
$ge = $order($list2[0], $list1[0]);
|
|
if (! $le) {
|
|
if (! $ge) {
|
|
// impossible: badly defined order
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
if (! $ge) {
|
|
return true;
|
|
}
|
|
else {
|
|
return order_lexicographic(
|
|
$order,
|
|
array_slice($list1, 1),
|
|
array_slice($list2, 1)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
?>
|