PHP Classes

File: Calculus.php

Recommend this page to a friend!
  Classes of Martin Lacroix   PHP Minifier   Calculus.php   Download  
File: Calculus.php
Role: Auxiliary data
Content type: text/plain
Description: Example : Class to minify
Class: PHP Minifier
Reduce the size of PHP source files
Author: By
Last change:
Date: 13 years ago
Size: 8,972 bytes
 

Contents

Class file image Download
<?php /** * Copyright (C) 2011+ Martin Lacroix * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * @license GNU General Public License Version 3 (GPLv3) * @link http://www.gnu.org/licenses/ */ /** * PHP_VER : 5.3+ * LIBRARIES : BCMATH * KEYWORDS : NUMBER DISTINCT UNIQUE DIGIT RANGE LIMIT LEADING ZERO * NOMBRE DISTINCT UNIQUE DIGIT CHIFFRE BORNE LIMITE ZERO SIGNIFICATIF * * Class mainly computing the number of numbers with unique digits over a range. * Include the option of leading zero * * Classe en charge principalement du calcul du nombre de nombres * constitués de digits uniques qu'il est possible de créer entre deux limites * Gère l'option des zéros significatifs du début * * @package tools * @version 1.0.1 * @author Martin Lacroix */ class Calculus { const GREATEST_NUMBER_WITH_UNIQUE_DIGITS = '9876543210'; /** * Calcule et renvoie le nombre de nombres constitués uniquement de digits uniques * qu'il est possible de créer entre deux limites * Si les zéros du début sont significatifs alors la limite ayant la longueur la plus courte * sera complétée par des 0 afin d'atteindre la longueur de la limite la plus longue * Ex : si min = 000000 et max = 999 => $max = 000999 * @param mixed $min Tout entier positif ou nul * @param mixed $max Tout entier positif ou nul - Automatiquement plafonné à 9876543210 * @param bool $leadingZeros Tenir compte des zéros du début * @return int */ static function nbOfNumbersWithUniqueDigits($min, $max = self::GREATEST_NUMBER_WITH_UNIQUE_DIGITS, $leadingZeros = TRUE) { // valeurs numériques obligatoires if ( ! (ctype_digit("$min") || ctype_digit("$max"))) { return 0; } // plafonnement de la limite supérieure if (bccomp($max, self::GREATEST_NUMBER_WITH_UNIQUE_DIGITS) == 1) { $max = self::GREATEST_NUMBER_WITH_UNIQUE_DIGITS; } $comp = bccomp($min, $max); if ($comp === 1) { // minimum > maximum return 0; } else if ($comp === 0) { // minimum = maximum return (self::hasUniqueDigits($min)) ? 1 : 0; } $nb = 0; $min = "$min"; $max = "$max"; // si zéros du début significatifs => égalise les longueurs des limites (complétion avec 0) $equalizeLength = function() use (&$min, &$max) { if (strlen($min) < strlen($max)) { $min = str_pad($min, strlen($max), '0', STR_PAD_LEFT); } else if (strlen($min) > strlen($max)) { $max = str_pad($max, strlen($min), '0', STR_PAD_LEFT); } }; // si les zéros du début sont significatifs, on égalise les longueurs des limites if ($leadingZeros) { $equalizeLength(); } // traitement des effets de bord (limite inférieure) : si dernier chiffre = 9 // vérification manuelle de la validité puis on rajoute 1 if ($min[strlen($min) - 1] == 9) { if (self::hasUniqueDigits($min)) { ++$nb; } $min = bcadd($min, 1); } // traitement des effets de bord (limite supérieure): si dernier chiffre = 0 // vérification manuelle de la validité puis on retranche 1 if ($max[strlen($max) - 1] == 0) { if (self::hasUniqueDigits($max)) { ++$nb; } $max = bcsub($max, 1); } // pour le traitement on égalise les longueurs des limites si cela n'a pas déjà été fait if (FALSE === $leadingZeros) { $equalizeLength(); } // détermination des paliers pour le comptage des valeurs $steps = self::steps($min, $max); foreach($steps as $step) { $nb += self::countAllowedNumbers($step[0], $step[1], $leadingZeros); } return $nb; } /** * Indique si le nombre en paramètre n'est composé que de digits uniques * @param mixed $p Tout entier positif * @return bool */ static function hasUniqueDigits($p) { return (ctype_digit("$p")) ? (count(array_flip(str_split($p, 1))) === strlen($p)) : FALSE; } /** * Décompose un nombre en paliers successifs de type x000->y999 * @param mixed $mixRange Tout entier positif ou nul * @param mixed $maxRange Tout entier positif ou nul * @return array Array([] => array(0 => min, 1 => max)) */ static private function steps($minRange, $maxRange) { /* 2 sens de parcours : croissant et décroissant Paliers si $pMin = 245600 et $pMax = 322799 245600 -> 245609 :: sens croissant 245610 -> 245699 :: " 245700 -> 245999 :: " 246000 -> 249999 :: " 250000 -> 299999 :: " 300000 -> 319999 :: sens décroissant (300000 = base minimum pour l'itération décroissante) 320000 -> 321999 :: " 322000 -> 322699 :: " 322700 -> 322789 :: " 322790 -> 322799 :: " */ $descendingSearch = FALSE; $len = strlen($minRange); $i = $len; $min = $minRange; $base = $minRange; // utilisé comme minimum pour la décomposition décroissante $steps = array(); while (--$i >= 0) { $left = substr($min, 0, $i); $right = str_repeat('9', $len - $i); $max = $left . $right; $comp = bccomp($maxRange, $max); if ($comp >= 0) { $steps[] = array($min, $max); $min = bcadd($max, 1); $min = str_pad($min, $len, '0', STR_PAD_LEFT); $base = $min; if ($comp === 0) { break; } } else { $descendingSearch = TRUE; break; } } if ($descendingSearch) { $i = $len; $max = $maxRange; while (--$i >= 0) { $left = substr($max, 0, $i); $right = str_repeat('0', $len - $i); $min = $left . $right; $comp = bccomp($min, $base); if ($comp >= 0) { $steps[] = array($min, $max); if ($comp === 0) { break; } $max = bcsub($min, 1); } else { $steps[] = array($base, $max); break; } } } return $steps; } /** * Routine de comptage du nombre de nombres à digits uniques * qu'il est possible de créer entre deux limites * @param mixed $min Tout entier positif ou nul * @param mixed $max Tout entier positif ou nul * @param mixed $leadingZeros Zéros de début significatifs * @return int */ static private function countAllowedNumbers($min, $max, $leadingZeros) { $nbAllowed = array(); // suppression des zéros du début non significatifs if (FALSE === $leadingZeros) { $min = ($min == 0) ? '0' : ltrim($min, '0'); $max = ($max == 0) ? '0' : ltrim($max, '0'); } $iMax = strlen($min) - 1; // préparation du tableau d'analyse pour chaque position de digit for($i = 0 ; $i <= $iMax ; ++$i) { $structure[$i] = array('possible' => array(), 'forbidden' => array(), 'substract' => 0); } $i = -1; while(++$i <= $iMax) { $structure[$i]['possible'] = range($min[$i], $max[$i]); if ($min[$i] == $max[$i]) { // propagation de la valeur interdite à toutes les positions postérieures for($j = $i + 1 ; $j <= $iMax ; ++$j) { $structure[$j]['forbidden'][] = $min[$i]; } } else { // propagation de la valeur choisie à toutes les positions postérieures for($j = $i + 1 ; $j <= $iMax ; ++$j) { ++$structure[$j]['substract']; } } } // comptage du nombre de valeurs possibles par position de digit foreach($structure as $value) { $nbAllowed[] = count(array_diff($value['possible'], $value['forbidden'])) - $value['substract']; } return (($nb = array_product($nbAllowed)) > 0) ? $nb : 0; } } ?>