Changeset 3878


Ignore:
Timestamp:
05/11/09 01:59:59 (4 years ago)
Author:
GilmarSantosJr
Message:

Item1574: improved TML notation of documentation, to get better results in System/PerlDoc

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/core/lib/Foswiki/Prefs/Stack.pm

    r3877 r3878  
    3535   * A final hash that maps preferences to the level they were finalized. 
    3636 
    37 This class deals with this stuff and must be used only by Foswiki::Prefs. 
     37This class deals with this stuff and must be used only by =Foswiki::Prefs= 
    3838 
    3939=cut 
     
    328328=begin TML 
    329329 
    330 ---++ Mathematical Considerations 
     330#MathStuff 
     331---+ Mathematical Considerations 
    331332 
    332333The bitmap is built in an way to meet two properties: 
     
    336337Preference levels 0-7 are in the first byte. 8-15 in the second and so on. If a 
    337338preference is defined in levels 2 and 7, for example, its bitmap will have 
    338 length 1, even if the stack is in 30th level. This is what "minimal possible 
    339 length" means. 
     339length 1, even if the stack is in 30th level.  
     340This is what _minimal possible length_ means. 
    340341 
    341342These two properties implies that the last byte of a bit string is non-zero. 
    342343 
    343 --- 
     344---++ Getting/Setting preferences with at most 8 levels 
    344345 
    345346Let's consider the first scenario: at most 8 preference values. This means that 
     
    349350listed property above.  
    350351 
    351 This implies that I can *always* take the logarithm of ord($map). (III) 
    352  
    353 The question is: given a bitstring, what is the highest level containing a 1? 
     352This implies that I can *always* take the logarithm of =ord($map)=. (III) 
     353 
     354The question is:  
     355__given a bitstring, what is the highest level containing a 1?__ 
     356 
    354357To answer this question let's consider the following mathematical expressions: 
    355 (log2(X) means "the logarithm of X in base 2") 
    356  
     358(=log2(X)= means _the logarithm of X in base 2_) 
     359 
     360<verbatim> 
    357361log2(1) = 0; 1 == 1 * 2 ** 0; 1 in base 2 is "00000001" (considering one byte) 
    358362log2(2) = 1; 2 == 1 * 2 ** 1; 2 in base 2 is "00000010" (considering one byte) 
    359363log2(4) = 2; 4 == 1 * 2 ** 2; 4 in base 2 is "00000100" (considering one byte) 
    360364log2(8) = 3; 8 == 1 * 2 ** 3; 8 in base 2 is "00001000" (considering one byte) 
     365</verbatim> 
    361366 
    362367Also notice that: 
    363 2 ** B <= X < 2 ** (B + 1) implies B <= log2(X) < (B + 1). This implies 
    364 int(log2(X)) == B for any X in the rage. Some examples: 
    365  
     368 
     369<verbatim> 
     3702 ** B <= X < 2 ** (B + 1) implies B <= log2(X) < (B + 1)  
     371</verbatim> 
     372 
     373This implies: 
     374 
     375<verbatim> 
     376int(log2(X)) == B, for any X in the above rage. 
     377</verbatim> 
     378 
     379 Some examples: 
     380 
     381<verbatim> 
    366382int(log2(3)) = log2(2) = 1; 3 in base 2 is "00000011" (considering one byte) 
    367383int(log2(5)) = log2(4) = 2; 5 in base 2 is "00000101" (considering one byte) 
     
    369385int(log2(7)) = log2(4) = 2; 7 in base 2 is "00000111" (considering one byte) 
    370386int(log2(9)) = log2(8) = 3; 9 in base 2 is "00001001" (considering one byte) 
     387</verbatim> 
    371388 
    372389The position of least significant bit is 0 and the position of the most 
    373 significant bit is 8, then: int(log2(X)) is the position of the 
     390significant bit is 8, then: =int(log2(X))= is the position of the 
    374391highest-significant bit equal to 1.  This always holds. The complete 
    375392mathematical proof is left as an exercise. 
    376393 
    377 Back to the question "given a bitstring, what is the highest level containing a 
    378 1?", it's clear the answer is: int(log2(X)).  
    379  
    380 X is the number corresponding to the bitstring character, so X == ord($map).  
    381  
    382 Also, log2(Y) == ln(Y)/ln(2) for any Y real positive.  
    383  
    384 Then we have: int(log2(X)) == int( ln( ord($map) ) / ln(2) ). 
    385  
    386 Conclusion: considering (III) and at most 8 levels I can figure out in 
    387 which level a preference is defined with the following O(1) operation:  
    388  
     394Back to the question __what is the highest level containing a 1?__ 
     395 
     396It's clear the answer is: =int(log2(X))=.  
     397 
     398=X= is the number corresponding to the bitstring character, so X = =ord($map)=. 
     399 
     400Also,  
     401<verbatim> 
     402log2(Y) == ln(Y)/ln(2), for any Y real positive 
     403</verbatim> 
     404 
     405Then we have:  
     406<verbatim> 
     407int(log2(X)) == int( ln( ord($map) ) / ln(2) ) 
     408</verbatim> 
     409 
     410*Conclusion*: considering (III) and at most 8 levels I can figure out in 
     411which level a preference is defined with the following _O(1)_ operation:  
     412 
     413<verbatim> 
    389414$defLevel = int( ln( ord($map) ) / ln(2) ); 
    390  
    391 --- 
     415</verbatim> 
     416 
     417---++ Getting/Setting preferences with arbitrary number of levels 
    392418 
    393419But preferences may have far more levels than 8. Now let's consider this 
    394 general case. We'll reduce it to the "at most 8 levels" case. 
     420general case. We'll reduce it to the _at most 8 levels_ case. 
    395421 
    396422At this point I must consider how perl built-in function =vec= works: 
     423<verbatim> 
    397424$a = ''; 
    398425vec($a,  0, 1) = 1; print unpack("b*", $a);  # "10000000" 
     
    400427vec($a,  7, 1) = 1; print unpack("b*", $a);  # "10100001" 
    401428vec($a, 16, 1) = 1; print unpack("b*", $a);  # "1010000100000000100000000" 
     429</verbatim> 
    402430 
    403431The least significant bit is the bit 0 of the first byte. The most significant 
    404 bit is the bit 7 of the last byte. unpack with "b*" gives us this 
     432bit is the bit 7 of the last byte. =unpack= with ="b*"= gives us this 
    405433representation, that is different from the one we're used to, but it's only a 
    406434representation. Test for yourself: 
    407435 
     436<verbatim> 
    408437$a = ''; 
    409438vec($a, 0, 1) = 1; print ord($a); #   1 
    410439vec($a, 2, 1) = 1; print ord($a); #   5 
    411440vec($a, 7, 1) = 1; print ord($a); # 133 
    412  
    413 Since ord() operates with one character (or the first one, if length($a) > 1), 
    414 we have to figure out a way to deal with preferences bigger than 8 levels.  
     441</verbatim> 
     442 
     443Since =ord()= operates with one character (or with the first one, if 
     444=length($a) > 1=), we have to figure out a way to deal with preferences bigger 
     445than 8 levels.  
    415446 
    416447The level to consider in order to get a preference value is the highest in 
     
    420451 
    421452Since (IV) holds, we can reduce the general case to the restricted case as 
    422 follows: we calculate the level considering the last byte. We'll get L in 
    423 [0,7].  Then we transform this value to the correct, considering that the bit 0 
    424 of the last byte is the bit (N - 1) * 8 of the general string, where N is the 
    425 total number of bytes. Examples: 
    426  
     453follows: we calculate the level considering the last byte. We'll get =$L= in 
     454=[0,7]=.  Then we transform this value to the correct, considering that the bit 
     4550 of the last byte is the bit =(N - 1) * 8= of the general string, where =N= is 
     456the total number of bytes. Examples: 
     457 
     458<verbatim> 
    4274591  byte: bit 0 of the last byte is bit (1 - 1) * 8 == 0  of the string 
    4284602 bytes: bit 0 of the last byte is bit (2 - 1) * 8 == 8  of the string 
    4294613 bytes: bit 0 of the last byte is bit (3 - 1) * 8 == 16 of the string 
     462</verbatim> 
     463 
    430464and so on. 
    431465 
    432 So, considering the general case where $map has arbitrary length, the answer 
    433 to "what is the highest level containing a 1?" is: 
    434  
     466So, considering the general case where =$map= has arbitrary length, the 
     467*general answer* to __what is the highest level containing a 1?__ is: 
     468 
     469<verbatim> 
    435470$defLevel = int( log( ord( substr($map, -1) ) ) / ln(2) ) +  
    436471            (length($map) - 1) * 8; 
    437  
    438 =substr($map, -1)= is "the last byte of $map" and because of (I) it's non-zero, 
    439 so log( ord( substr($map,-1) ) ) exists.  Because of (II), length($map) is at 
    440 least 1. So this general expression is *always* valid. 
    441  
    442 --- 
     472</verbatim> 
     473 
     474=substr($map, -1)= is _the last byte of =$map=_ and because of (I) it's 
     475non-zero, so =log( ord( substr($map,-1) ) )= exists.  Because of (II), 
     476=length($map)= is at least 1. So this general expression is *always* valid. 
     477 
     478---++ Growth/Shrink operations with at most 8 levels 
    443479 
    444480There are growth and shrink operations on the stack and hence in the bitmaps. 
    445481These operations *must* keep (I) and (II). Let's consider the initial case: 
    446 $stack->{map} is an empty hashref, so both (I) and (II) holds. 
     482=$stack->{map}= is an empty hashref, so both (I) and (II) holds. 
    447483 
    448484The addition of a preference uses =vec()=, that expands the string as (and only 
     
    455491 
    456492Considering at most 8 bytes, let's assume we want to restore to the level 5. 
    457 Notice that: 2 ** (5 + 1) == 64 == "01000000". 64 - 1 == 63 == "00111111". Bits 
    458 0-5 are 1 and all others are 0. Since (1 & X) == X and (0 & X) == 0, we can 
    459 build a mask using this process and apply it to the map and we'll get the 
    460 bitmap restored to the desired level. So, in order to restore to level L we 
    461 build a mask as ((2 ** (L+1)) - 1) and perform:  
    462  
    463 $map &= $mask;  
     493Notice that: 
     494 
     495<verbatim> 
     4962 ** (5 + 1) == 64 == "01000000" 
     49764 - 1 == 63 == "00111111" 
     498</verbatim> 
     499 
     500Bits 0-5 are 1 and all others are 0.  
     501 
     502And Since: 
     503 
     504<verbatim> 
     505(1 & X) == X 
     506(0 & X) == 0 
     507</verbatim> 
     508 
     509we can build a mask using this process and apply it to the map and we'll get 
     510the bitmap restored to the desired level. So, in order to restore to level =$L= 
     511we build a mask as =((2 ** (L+1)) - 1)= and perform:  
     512 
     513<verbatim> 
     514$map &= $mask; 
     515</verbatim> 
    464516 
    465517If the result is 0, we need to remove that preference from the hash, so both 
    466518(I) and (II) holds. 
    467519 
    468 Now considering the general case: if we want to restore to level L, we need to 
    469 build a mask whose bits 0-L are 1. This mask will have length int($L/8) + 1: 
    470  
     520---++ Growth/Shrink operations with arbitrary number of levels 
     521 
     522Now considering the general case: if we want to restore to level =$L=, we need 
     523to build a mask whose bits 0-L are 1. This mask will have =int($L/8) + 1= 
     524bytes. 
     525 
     526<verbatim> 
    4715270  <= $L <  8 implies the mask 1-byte  long. 
    4725288  <= $L < 16 implies the mask 2-bytes long. 
    47352916 <= $L < 32 implies the mask 3-bytes long.  
     530</verbatim> 
    474531 
    475532and so on. We conclude that all bytes of the mask, except the last, will be 
    476 \xFF (all bits 1). If we map $L to [0,7], then we have the restricted case 
     533=\xFF= (all bits 1). If we map =$L= to [0,7], then we have the restricted case 
    477534above. 
    478535 
    479 The number of bytes except the last in the bitstring is int($L/8). The bit 
    480 position of $L in the last byte is ($L % 8): 
    481  
     536The number of bytes except the last in the bitstring is =int($L/8)=. The bit 
     537position of =$L= in the last byte is =($L % 8)=: 
     538 
     539<verbatim> 
    482540Level  8 corresponds to bit 0 of the second byte. int(8/8)  = 1.  8 % 8 = 0. 
    483541Level  9 corresponds to bit 1 of the second byte. int(9/8)  = 1.  9 % 8 = 1. 
    484542Level 15 corresponds to bit 7 of the second byte. int(15/8) = 1. 15 % 8 = 7. 
    485543Level 16 corresponds to bit 0 of the third byte.  int(16/8) = 2. 16 % 8 = 0. 
     544</verbatim> 
    486545 
    487546So the general way to build the mask is: 
    488547 
     548<verbatim> 
    489549$mask = ("\xFF" x int($L/8)); # All bytes except the last have all bits 1. 
    490550$mask .= chr( ( 2**( ( $L % 8 ) + 1 ) ) - 1 ); # The last byte is built based 
    491551                                               # on the restricted case above. 
    492  
    493 The $mask has the minimal possible length, cause the way it's built. $map & 
    494 $mask has at most length($mask) bytes, cause the way & works. But we still must 
    495 guarantee (I) and (II), so we need to purge the possible zero-bytes in the end 
    496 of the bitstring: 
    497  
     552</verbatim> 
     553 
     554The =$mask= has the minimal possible length, cause the way it's built.  
     555=$map & $mask= has at most =length($mask)= bytes, cause the way =&= works. But 
     556we still must guarantee (I) and (II), so we need to purge the possible 
     557zero-bytes in the end of the bitstring: 
     558 
     559<verbatim> 
    498560while (ord(substr($map, -1)) == 0 && length($map) > 0 ) { 
    499561    substr($map, -1) = ''; 
    500562} 
    501  
    502 We need to test if length($map) is greater than 0, otherwise we may enter on an 
    503 infinite loop, if all bytes of the result are 0. 
    504  
    505 This =while= guarantee (I) above. Then we check if the resulting $map has 
    506 length 0. If so we remove the pref from the hash, so (II) is achieved. 
    507  
    508 ---++ Other Considerations 
    509  
    510 This implementation is more complex than the "natural" way, but using this we 
    511 avoid to have more than one copy of preference values and this architecture 
    512 (index separated from the values) make it easy to change the way values are 
    513 stored. And consider it's slow to copy large chunks of data around. All copied 
    514 values in this architecture are far smaller than the preferences values (a 
    515 typical big bitstring has less than 4 bytes, while a preference value is bigger 
    516 than this). 
     563</verbatim> 
     564 
     565We need to test if =length($map)= is greater than 0, otherwise we may enter on 
     566an infinite loop, if all bytes of the result are 0. 
     567 
     568This =while= guarantee (I) above. Then we check if the resulting =$map= has 
     569length 0. If so we remove the pref from the hash, so (II) is also achieved. 
     570 
     571---+ Other Considerations 
     572 
     573This implementation is more complex than the "natural" way, but using this: 
     574   * We avoid to have more than one copy of preference values 
     575   * This architecture (index separated from the values) make it easy to change 
     576     the way values are stored.  
     577 
     578Also, consider it's slow to copy large chunks of data around. All copied values 
     579in this architecture are far smaller than the preferences values (a typical big 
     580bitstring has less than 4 bytes, while a preference value is bigger than this). 
    517581 
    518582=pack= and =unpack= are not used cause they are not needed and cause the way to 
Note: See TracChangeset for help on using the changeset viewer.