|
Conținut
LongNum.php
/*! ----------------------------------------------- * * Clasa LongNum (Long Numbers) * * @Author: Dumitru Uzun (DUzun) * @Web : http://duzun.teologie.net/ * * * ----------------------------------------------- */ /*------------------------------------------------------------------------*/ include_once(dirname(__FILE__).DIRECTORY_SEPARATOR.'PkInts.php'); /*------------------------------------------------------------------------*/ class LongNum extends PkInts { /* $radix indica baza in care se reprezinta numarul la convertirea in/din STRING. * La convertirea in numar, $radix == 1 indica date binare (fara transformare). * Simbolurile cifrelor sunt cifrele zecimale si literele alfabetului Englez (36 simboluri). */ //static $digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; static public $radix = 10; // baza implicitta static public $carry = 0; // Valoarea de transport /*------------------------------------------------------------------------*/ /*! Obtine cifra pentru valoarea $nm (in baza 36) */ static function val2digi($nm) { //return self::$digits[$nm]; return chr((($r=$nm+0x30)>0x39)?($r+0x07):$r) ; } /*! Obtine valoarea cifrei $ch (in baza 36) */ static function digi2val($ch) { //return strpos(self::$digits, strtoupper($ch)); $ch = ord($ch[0]); if(0x2F<$ch && $ch<0x3A) return $ch-0x30; // cifrele zecimale $ch |= 0x20; // fortam minuscula if(0x60<$ch && $ch<0x7B) return $ch-0x57; // alfabetul latin return false; // nu exista asa cifra } /*! Incearca sa ghiceasca baza numarului din reprezentarea $str * Daca sunt caractere straine, $str se considera binar. * Daca incepe cu '0x', baza e 16. * Daca incepe numai cu '0', baza e 8. * Daca incepe cu '$', baza e 2. * Daca sunt numai cifre zecimale, baza e 10. */ static function guess_radix($str) { if(!strlen($str) || preg_match('#[^\s\+\-\$0-9A-Z]#i', $str)) return 1; // binarry data $p = 0; while(preg_match('#^\s\+\-#', $str[$p])) $p++; switch($str[$p]) { case '0': if($str[$p+1] == 'x') return 16; return 8; case '$': return 2; default: if(!preg_match('#[^0-9]#', $str)) return 10; } return false; } /*------------------------------------------------------------------------*/ /*! $val - valoarea, $radix - baza lui $val. * $radix == 1 - pentru $val date binare */ function __construct(/*[$val=0[, $radix]]*/) { parent::__construct(); if(func_num_args()) { if(func_num_args() > 1) { $radix = (int)func_get_arg(1); if( 2 <= $radix && $radix <= 36 ) $this->radix = $radix; } else $radix = 0; $val = func_get_arg(0); if($radix) $this->set_val($val, $radix); else $this->set_val($val); } } function __toString() { return $this->get_str(); } /*------------------------------------------------------------------------*/ protected function get_radix() { // Se apeleaza automat prin getter return self::$radix; } /*------------------------------------------------------------------------*/ /*! * Valoarea string a obiectului este reprezentarea numarului in baza $radix. * Daca $unsigned == TRUE, numrul se interpreteaza ca fara semn. */ public function get_str($radix=false, $unsigned=false) { if(!$radix) $radix = $this->radix; $rev = $radix < 0 ? $radix = -$radix : false; if($radix == 1) return $rev ? strrev($this->data) : $this->data; $l = strlen($this->data); if($l==0) return '0'; // '0' e zero in orice baza $data = $this->data; // copie de rezerva a datelor if($unsigned || !$this->get_sign()) $s = 0; else { $s = -1; $this->negative(); } switch($radix) { case 2: $r = rtrim($this->get_bits(), '0 '); // ordinea inversata (din mem) $rev = !$rev; if($s) { $r .= '-'; $s = 0; } break; case 16: $r = ''; $p = $this->not_null_r(); // stim la sigur ca numarul e pozitiv do $r .= bin2hex($this->data[$p]); while($p--); $r = ltrim($r, '0'); break; default: $r = ''; //if(!( $radix & ($radix - 1) )) {} // baza 2^n ??? while(!$this->is_null()) { $this->div_int($radix); // impartim numarul la baza $r = self::val2digi(self::$carry) . $r; // restul impartirii este ultima cifra } } $this->data = $data; // restabilim datele if(empty($r)) return '0'; if($s) $r = '-'.$r; return $rev ? strrev($r) : $r; } /*------------------------------------------------------------------------*/ /*! * Daca $radix == 0, se foloseste baza $this->radix. * Daca $radix == 1, $op sunt date binare. * Daca $radix e o baza valida, $op reprezinta inscrierea * unui numar in baza $radix. */ public function set_str(&$op, $radix = 0) { if(!is_string($op)) $op = strval($op); if( $radix < 0 ) { $radix = -$radix; $op = strrev($op); } if($radix == 0) { $r = self::guess_radix($op); $radix = $r ? $r : self::default_radix; } elseif($radix == 1) $this->data = $op; else { $this->data = ''; if($op[0] == '-') $s = true; $op = ltrim(str_replace(' ', '', $op), '-+$'); $l = strlen($op); $v = 0; if(!( $radix & ($radix - 1) )) // Baza 2^n { $r = max_log2($radix); // nr de biti pu o cifra $radix $o = 0; while($l--) // nr se parcurge invers, deoarece in memorie se reprezinta invers { $d = self::digi2val($op[$l]); if($d === false || $d >= $radix) continue; // cifra inacceptabila $v += $d << $o; // acumulatorul valorii for($o+=$r; $o>=8; $o-=8, $v>>=8) // $o - acumulatorul nr de biti $this->data .= chr($v & BYTE_MASK); } if($v) $this->data .= chr($v); } else // Baza oarecare, cazul general { $r = floor(log(INT_LO_MASK, $radix)); // nr de cifre in baza $radix ce incap in jumate de INT if($l < 2*$r) // pentru numere mice nu are rost de pierdut timpul cu inmultiri { for($i=0; $i<$l; $i++) { $d = self::digi2val($op[$i]); if($d === false || $d >= $radix) continue;// cifra inacceptabila $v *= $radix; $v += $d; // acumulatorul valorii } $this->set_int($v); } else { $m = pow($radix, $r); // baza pu $r cifre grupate $o = $r; for($i=0; $i<$l; $i++) { $d = self::digi2val($op[$i]); if($d === false || $d >= $radix) continue;// cifra inacceptabila $v *= $radix; $v += $d; // acumulatorul valorii $o--; // numaratorul cifrelor bazei $radix if(!$o) { $this->mul_int($m); $this->add_int($v); $v = 0; $o = $r; } } if($v) { // nu a fost adaugata valoarea $v la numar $this->mul_int(pow($radix, $r-$o)); $this->add_int($v); } } } $this->data .= NULL_CHAR; // reprezentarea totdeauna este nr pozitiv if(!empty($s)) $this->negative(); } return $this->get_val(); } /*------------------------------------------------------------------------*/ /* Important: * $by trebuie sa fie atat de mare, incat ($by-1) * 256 + 255 <= PHP_INT_MAX, * de unde avem: $by <= (INT_MAX-255) / 256 + 1 = (INT_MAX >> 8) + 1 */ public function div_int($by) { if($by == 0) { return self::$carry = -1; } if($s = $by < 0) // !!! exceptia! { $this->negative(); $by = -$by; } self::$carry = 0; if($by > 1) { $o = &self::$carry; $dt = &$this->data; $l = strlen($dt); while($l--) { $g = ($o << 8) | ord($dt[$l]); // transportul de la octetul precedent + octetul curent $o = $g % $by; // transportul pentru urmatorul octet $dt[$l] = chr(($g-$o) / $by); // catul impartirii } } return $this->get_val(); } // div_int /*------------------------------------------------------------------------*/ /* Important: * Analogic cu div_int, $o <= INT_MAX, de unde avem: * $by <= (INT_MAX-(INT_MAX / 256)) / 255 = (INT_MAX >> 8) + 1 */ public function mul_int($by, $null_check=true) { // 0 * x = 0, for any x if($by == 0) { $this->data = ''; return 0; } if($null_check && $this->not_null() === false) return 0; // $this->reduce(); // sa presupunem ca algoritmii precedenti au grija de acest lucru $s = $by & INT_SIGN; $o = $this->get_sign(); if($o) { $this->negative(); $s = ($s xor $o); } if($s) $by = -$by; // de cazul -$by == $by are grija maska de mai jos if($by & INT_HI_MASK) // Daca factorul depaseste jumate de INT, efectuam 2 inmultiri { $o = clone $this; // pentru fiecare jumatate de $by trebuie cate un factor $this->mul_int(($by >> INT_HALF_BIT_SIZE) & INT_LO_MASK, false); // Inmultim partea de sus a $by cu $this $this->data = str_repeat(NULL_CHAR, INT_SIZE >> 1) . $this->data; // deplasam $this in sus cu jumatate de INT $o->mul_int($by & INT_LO_MASK, false); // Inmultim partea de jos a $by cu $this $this->add_obj($o); // adaugam produsul pentru jumatatea de jos unset($dt); } else { $o = 0; $l = strlen($this->data); $dt = &$this->data; for($i=0; $i<$l; $i++, $o >>= 8) { $o = ord($dt[$i]) * $by + $o; $dt[$i] = chr($o & BYTE_MASK); } for(; $o; $o >>= 8) $dt .= chr($o & BYTE_MASK); $dt .= NULL_CHAR; // Numarul trebuie sa fie pozitiv } if($s) $this->negative(); return $this->get_val(); } // mul_int /*------------------------------------------------------------------------*/ function mul_obj($op) { $a = &$op->data; // se presupune ca $op este mai scurt decat $this $b = &$this->data; $al = strlen($a); $bl = strlen($b); $s = ord($a[$al-1]) & BYTE_SIGN; $m = ord($b[$bl-1]) & BYTE_SIGN; if($s) { $r = $a; // backup $op $op->negative(); $op->data = &$r; // restore $op if(self::$carry < 0) $al++; } if($m) { $this->negative(); $s ^= $m; // result sign } if(self::$carry < 0) $bl++; if($bl < $al) { // $b trebuie sa fie numarul mai lung $r = &$a; $a = &$b; $b = &$r; $o = $al; $al = $bl; $bl = $o; } unset($r); $o = 0; $r = ''; // rezultatul inmultirii $l = $al + $bl - 1; global $mxln; for($k=0; $k<$l; $k++) { $m = min($k+1, $al); $sum = 0; for($i=max(0, $k+1-$bl); $i<$m; $i++) { $sum++; $j = $k-$i; $o += ord($a[$i]) * ord($b[$j]); // length($o) <= (2*$al) bytes } if($sum > $mxln) $mxln = $sum; $r .= chr($o & BYTE_MASK); $o >>= 8; } while($o) { // transportul prelungeste numarul $r .= chr($o & BYTE_MASK); $o >>= 8; } $this->data = &$r; if($s) $this->negative(); self::$carry = $o; return $this->get_val(); } // mul_obj /*------------------------------------------------------------------------*/ public function set_val($val) { $this->data = ''; if(is_int($val) || is_bool($val)) return $this->set_int((int)$val); // datele constituie un singur INT if(is_float($val)) $this->set_float($val); else if(is_array($val)) parent::set_ints($val); else // un array de numere intregi pentru un numar lung if($val instanceof parent) return $this->set_obj($val); else { $radix = func_num_args() > 1 ? func_get_arg(1) : $this->radix; $this->set_str($val, $radix); } return $this->get_val(); } protected function get_val() { $this->reduce(); return $this->reduce() <= PHP_INT_SIZE ? $this->get_int_s() : $this; } /*------------------------------------------------------------------------*/ public function add($op) { if(is_int($op) || is_bool($op)) return $this->add_int((int)$op); if(is_float($op)) return $this->add_float($op); if(is_array($op)) { $radix = func_num_args() > 1 ? func_get_arg(1) : 0; foreach($op as &$v) $this->add($v, $radix); return $this->get_val(); } if( !($op instanceof self) ) { $o = new self(); $o->set_str($op, func_num_args() > 1 ? $radix=func_get_arg(1) : 0); $op = $o; } return $this->add_obj($op); } /*------------------------------------------------------------------------*/ public function mul($op) { if(is_int($op) || is_bool($op)) return $this->mul_int((int)$op); if(is_float($op)) return $this->mul_float($op); if(is_array($op)) { $radix = func_num_args() > 1 ? func_get_arg(1) : 0; foreach($op as &$v) $this->mul($v, $radix); return $this->get_val(); } if( !($op instanceof self) ) { $o = new self(); $o->set_str($op, func_num_args() > 1 ? $radix=func_get_arg(1) : 0); $op = $o; } return $this->mul_obj($op); } /*------------------------------------------------------------------------*/ /*! * Obtine opusul numarului * * Numerele intregi se reprezinta ca complementi fata de 2^n, * de aceea pentru a obtine valoarea negativa a numarului z, * putem aplica formula: * -z == ~z + 1, * unde ~z este complementul fata de 2^n a numarului z. * ~z = 2^n - 1 - z * * Observatie: ~z - 1 = ~(z + 1) */ function negative() { $dt = &$this->data; $l = strlen($dt); for($i=0; $i<$l && $dt[$i] == NULL_CHAR; $i++) ; // cat timp exista transport si nu s-a ajuns la sfarsit if($i < $l) { self::$carry = 0; // nu exista transport $dt[$i] = chr(BYTE_MASK & ~ord($dt[$i]) + 1); $i++; if($i == $l && ord($dt[$i-1]) == BYTE_SIGN) // cazul special, cand -n == n ! { $dt .= NULL_CHAR; // numarul a fost negativ si trebuie sa devina pozitiv self::$carry = -1; // pentru cazul de exceptie! } else for(; $i<$l; $i++) $dt[$i] = chr(BYTE_MASK & ~ord($dt[$i])); // cat timp nu s-a ajuns la sfarsit } else self::$carry = 1; // exista transport (doar pentru valoarea 0) return $this; } /*------------------------------------------------------------------------*/ /*! * Reduce octetii superiori, daca e posibil, fara a skimba valoarea numarului. * @return: Nr de octeti ramasi */ protected function reduce() // sign { $l = $this->get_size(); if(!$l) return 0; $g = $this->get_byte($l-1); if($g === 0 || $g === BYTE_MASK) // "cifra" superioara e 0 sau ~0 { $l = $this->ltrim_byte($g); if(!$l) // s-au redus toti octetii { $this->push_byte($g); // adaugam octetul purtator de semn $l = 1; // a ramas un singur octet } else { $v = $this->get_byte($l-1); // ultimul octet ramas if( BYTE_SIGN & ($v ^ $g) ) { $this->push_byte($g); // ultima "cifra" trebuie sa pastreze semnul $l++; } } } return $l; } /// Nr de octeti nenuli din numar -1 function grad() { return $this->not_null_r(); } /*------------------------------------------------------------------------*/ protected function set_float($op) { /* Reprezentarea pe float difera calitativ, de aceea vom * folosi metode numerice (nu reprezentarea numarului) * de convertire a valorii in formatul intern LongNum. * float == double (mantisa are 52 bitsi) */ if(PHP_INT_SIZE == 8) return $this->set_int($op); // in acest caz INT il poare contine pe float // semnul numarului se prelucreaza separat if($op<0) {$op = -$op; $s = -1; } else $s = 0; $d = INT_LO_MASK + 1; // sqrt(MAX_INT) - jumate din capacitatea INT (ex. pe 32 biti - 0x00010000) $isz = PHP_INT_SIZE >> 1; // nr de octeti necesari pentru jumate de INT while($op) { $f = floor($op / $d); // jumate de INT (binar) $this->data .= int2bs($op - $f * $d, $isz); // restul impartirii $op = floor($f / $d); // a doua jumate de INT $this->data .= int2bs($f - $op * $d, $isz); // al doilea rest al impartirii } if($s) $this->negative(); return $this->get_val(); } protected function set_obj($op) { if($op instanceof parent) // daca obiectul atribuit este de acelasi tip, $this->data = $op->data; // pur si simplu se copie datele else // in caz contrar $this->set_str($op); // se incearca atribuirea valorii STRING a $op return $this->get_val(); } protected function get_obj() { return clone $this; } /// Un simplu alias pentru set_int($op, 0) protected function set_integer($op) { return $this->set_int($op); } protected function get_integer() { return $this->get_int_s(); } /*------------------------------------------------------------------------*/ protected function add_float(&$op) { // float == double (mantisa are 52 bitsi) if(PHP_INT_SIZE == 8) return $this->add_int($op); // in acest caz INT il poare contine pe float return $this->add_obj(new self($op)); } protected function add_int($op) { $o = new self(); $o->set_int($op); return $this->add_obj($o); } protected function add_obj($op) { $al = strlen($this->data); $bl = strlen($op->data); if($al < $bl) { // $a trebuie sa fie numarul mai lung $a = &$op->data; $b = &$this->data; $r = $al; $al = $bl; $bl = $r; } else { // $a este numarul cel mai lung $a = &$this->data; $b = &$op->data; } $r = ''; $carry = &self::$carry; $carry = 0; // transportul initial lipseste for($i=0; $i<$bl; $i++) // parcurgem partea din lungimea comuna a ambelor numere { $carry += ord($a[$i]) + ord($b[$i]); // adunarea "cifrelor" curente + transportul $rb = BYTE_MASK & $carry; // ultima "cifra" din suma curenta $r .= chr($rb); // scrie "cifra" in rezultat $carry >>= 8; // obtinem transportul } $gb = (ord($b[$bl-1]) & BYTE_SIGN) >> 7; // bitul de semn se plaseaza pe prima pozitie for(; $i<$al && ($gb ^ $carry); $i++) // $gb este bitul de semn (0 sau 1), iar $carry poate fi 0 sau 1 { $carry += ord($a[$i]) + $gb; // $gb reprezinta "cifrele" lui $b de dupa $bl $rb = BYTE_MASK & $carry; // ultima "cifra" din suma curenta $r .= chr($rb); // scrie "cifra" in rezultat $carry >>= 8; // obtinem transportul (max 1) } /* Cand apare depasirea capacitatii curente a numerelor? * 1. $a > 0 && $b > 0 && $r < 0, no carry * 2. $a < 0 && $b < 0 && $r > 0, carry * * Nota: Daca $a si $b au semne diferite, depasirea nu poate avea loc! */ if($i<$al) // $ga == $gr { $r .= substr($a, $i, $al-$i); } else { $gr = ($rb & BYTE_SIGN) ? BYTE_MASK : 0; if($gr != $gb) { $ga = ord($a[$al-1]) & BYTE_SIGN ? BYTE_MASK : 0; if($ga == $gb) $r .= chr($ga); } } $this->data = &$r; return $this->get_val(); // Cream obiectul numarului din $r $ro = new self(); $ro->data = &$r; return $ro; return $ro->get_val(); } // add_obj /*------------------------------------------------------------------------*/ /// Obtine semnul numarului: !0 pentru negativ si 0 pentru pozitiv protected function get_sign() { $l = parent::get_size(); $b = parent::get_byte($l-1); return ($b & BYTE_SIGN); } /// Seteaza semnul numarului: TRUE <-> -1; FALSE <-> 0; protected function set_sign($sgn) { if(!is_numeric($sng)) $sng = $sng ? BYTE_SIGN : 0; else $sng = $sgn < 0 ? BYTE_SIGN : 0; $osgn = $this->get_sign(); if($sgn ^ $osgn) // daca semnul cerut difera ce cel al numarului $this->negative(); // se obtine opusul numarului curent return $sgn; } /*------------------------------------------------------------------------*/ /// Verifica daca numarul este o putere a lui 2 (2^n). function is_pow2() { if($l = strlen($this->data)) { $dt = &$this->data; while($l--) if($dt[$l] != NULL_CHAR) { $i = ord($dt[$l]); if($i & ($i - 1)) return false; $i = $l; while($l--) if($dt[$l] != NULL_CHAR) return false; return $i+1; // pozitia octetului nenul } } return true; } // /*------------------------------------------------------------------------*/ // function negative_i() // { // $o = &self::$carry; // $v = $this->reset; // $o = 1; // if(!$v) // ~0 + 1 = 2^n-0 + 1 => exista transport // { // while($o && $v!==false) // cat timp exista transport si nu s-a ajuns la sfarsit // { // $v = ~$v; // // LO of $v // $o += INT_LO_MASK & $v; // $f = INT_LO_MASK & $o; // $o >>= INT_HALF_BIT_SIZE; // // // HI of $v // $o += INT_LO_MASK & ($v >> INT_HALF_BIT_SIZE); // $g = INT_LO_MASK & $o; // $o >>= INT_HALF_BIT_SIZE; // // $this->current = ($g << INT_HALF_BIT_SIZE) | $f; // $v = $this->next; // } // } else { $v -= $o; $o = 0; } // while($v!==false) // cat timp nu s-a ajuns la sfarsit // { // $this->current = ~$v; // $v = $this->next; // } // return $this; // } // /*------------------------------------------------------------------------*/ // protected function add_int_i($op) // { // self::$carry = 0; // transportul initial lipseste // $v = $this->reset; // $this->current = add_carry($v, $op, self::$carry); // $g = ($op & INT_SIGN) >> INT_BIT_SIZE-1; // $v = $this->next; // while($v !== false && ((self::$carry ^ $g) & 1)) // { // $this->current = add_carry($v, $g, self::$carry); // $v = $this->next; // } // unset($this->cursor); // return $this->get_val(); // } // /*------------------------------------------------------------------------*/ // function add_obj_i($op) // { // if($this->size > $op->size) { // $ho = $this; // $lo = $op; // } // else { // $lo = $this; // $ho = $op; // } // // self::$carry = 0; // transportul initial lipseste // $lv = $lo->reset; // $hv = $ho->reset; // while($lv !== false) // partea din lungimea comuna a ambelor numere // { // $this->current = add_carry($hv, $lv, self::$carry); // $lv = $lo->next; // $hv = $ho->next; // } // $g = $lo->sign; // while($hv !== false && (self::$carry ^ $g & 1)) // { // $this->set_int(add_carry($hv, $g, self::$carry), $ho->cursor); // $hv = $ho->next; // } // if($hv !== false && $ho != $this) // { // $l = $ho->len; // do $this->append_bin($ho->get_bin($ho->cursor++)); // while($ho->cursor < $l); // } // return $this->get_val(); // } // /*------------------------------------------------------------------------*/ // public function div_int_i($by) // { // if($by == 0) { return self::$carry = -1; } // self::$carry = 0; // if($by == 1) return $this; // $o = &self::$carry; // $v = $this->end; // while($v !== false) // { // // HI of $v // $w = ($o << INT_HALF_BIT_SIZE) | INT_LO_MASK & ($v >> INT_HALF_BIT_SIZE); // $o = $w % $by; // $f = (int)(($w-$o) / $by); // Scadem restul pentru a obtine int la impartire // // // LO of $v // $w = ($o << INT_HALF_BIT_SIZE) | INT_LO_MASK & $v; // $o = $w % $by; // $g = (int)(($w-$o) / $by); // Scadem restul pentru a obtine int la impartire // // $this->current = ($f << INT_HALF_BIT_SIZE) | $g; // $v = $this->prev; // } // return $this->get_val(); // } // div_int_i // /*------------------------------------------------------------------------*/ }; /*------------------------------------------------------------------------------------------*/ function ln_str($val, $radix=false) { if( ! $val instanceof LongNum ) $val = new LongNum($val); return $val->get_str($radix); } function ln_add($op1, $op2) { $r = new LongNum($op1); return $r->add($op2); } function ln_sub($op1, $op2) { $r = new LongNum($op2); $r->negative(); return $r->add($op1); } function ln_mul($op1, $op2) { $r = new LongNum($op1); return $r->mul($op2); } /*------------------------------------------------------------------------------------------*/
Aici acumulez programe şi algoritmi interesanti alcătuiţi de mine (cu mici excepţii) pe parcursul studierii unor limbaje de programare. Cea mai mare parte din ele sunt realizate în Pascal. Nu am scopul creării unui curs specializat sau a descrierii detaliate a anumitor limbaje, ci doar prezint informaţii utile, plus ceva exemple interesante...
Răsfoitorul de fișiere (File Browser):Codul sursă al programelor este organizat în mape şi fişiere. Paginile care conțin cod sursă de programe de regulă au un răsfoitor de fișiere în partea stangă a paginii reprezentat de un bloc cu titlul „File Browser”. Pentru a vizualiza un program sau conţinutul unei mape, faceţi click pe numele fişierului / mapei. Dacă fişierul nu este textual, el se va descărca automat, iar dacă este textual, conținutul lui se va afișa într-un bloc pe centrul paginii. Pentru a descărca un fişier, faceţi click pe dimensiunea (size) lui.
Căutare
|