This document explains in detail how the PAD sequence generation subsystem works.
The sequence subsystem generates mathematical sequences like prime numbers, Fibonacci numbers, triangular numbers, and many more. It supports 122 different sequence types, filtering, transformations, and various output actions.
A sequence tag enters through sequence/tag.php:
include PQ . 'inits/tag.php'; // The tag's name, parameters and options
include PQ . 'inits/inits.php'; // Variables, plays, actions, limits
include PQ . 'build/build.php'; // Generate the sequence
include PQ . 'exits/exits.php'; // Post-actions, storing, info
return include PQ . 'exits/return.php'; // The rows for the tag
The expression form sequence:<type>(n) enters through sequence/sequence.php, which runs
the same build for one term (inits/direct.php, inits/eval.php, build/inits.php,
inits/init.php, …, build/build.php, exits/actions.php) and returns the term at that
position, not a list - sequence:fibonacci(8) is 13. A type with a function.php answers the
position from its formula, without a walk; any other walks from its first term, and the terms
a lookup made are kept for the request (pqTermsMade), so a later lookup at or below them is
answered from them. A position the type does not reach within the limits below is '', and
under the strict check an error that names the limit.
The subsystem uses two path constants:
PQ - Path to the sequence subsystem root (pad/sequence/)PT - Path to sequence types (pad/sequence/types/)Parameters control how sequences are generated (inits/parms.php):
| Parameter | Default | Description |
|---|---|---|
from |
1 | Where the loop starts - a value for the types that test candidates, a position for the ones that compute the nth term |
to |
PHP_INT_MAX | Where the loop ends, counting the same way as from |
sole |
none | Generate single value (sets from=to) |
minimal |
PHP_INT_MIN | Minimum acceptable value |
maximal |
PHP_INT_MAX | Maximum acceptable value |
increment |
1 | Step between iterations, 1 or more - a range ('0..1') also counts in a step below 1 |
rows |
varies | Maximum number of results |
try |
varies | Maximum iterations to attempt |
stop |
PHP_INT_MAX | Stop when value reaches this |
skip |
0 | Skip the first N candidates offered - not the first N kept, so on a filtered sequence it drops fewer terms than N |
randomly |
’’ | Enable random selection |
unique |
’’ | Only unique values |
name |
’’ | Name for storing sequence |
build |
’’ | Override build type |
toData |
’’ | Store to data |
negative |
0 | Invert an action’s selection; has no effect on a play, which inverts by using remove instead of keep |
pull |
’’ | Pull from stored sequence |
push |
’’ | Push to stored sequence |
Four settings in pad/config/sequence.php bound a run:
| Setting | Default | What it bounds |
|---|---|---|
$padSeqDefaultRows |
10 | The terms a tag makes when it names no rows= and has no end of its own |
$padSeqDefaultTries |
10,000 | The candidates a filtered sequence tests when it names no try= - and how many terms membership (keep, remove, flag) makes of a type it has to look in |
$padSeqMaxTries |
1,000,000 | The ceiling of candidates: try=, stop=, to= and a position lookup go no further |
$padSeqMaxSeconds |
5 | The time a run walks its candidates |
The time is looked at between candidates. One candidate - a costly test, a formula at a far
position - and an action over the finished list are not cut off while they run, so it is no
deadline for the request; PHP’s max_execution_time is.
A run that ran out of candidates or time answers the terms it found, as one that came to its
end does: {sequence prime, rows=10, try=5} gives the three primes up to 5, and count after
it says 3, with no mark that seven are missing. A position lookup does say so - under the
strict check sequence:strong(5) is an error naming the limit it ran into.
A tag’s terms are made in full before its rows render - there is no lazy iteration; rows=,
page= and the limits decide how many are made.
The subsystem supports different build strategies (build/types/):
build/types/loop.php)Iterates from $pqFrom to $pqTo, calling the sequence’s loop.php file for each value:
$pqGo = $pqFrom;
$pqWalk = 'loop';
include PQ . 'build/one.php'; // the walk: $pqLoop = $pqGo, ..., $pqGo += $pqInc
build/types/fixed.php)Uses a pre-computed array from the sequence’s fixed.php file:
$pqFixed = include PT . "$pqSeq/fixed.php";
$pqWalk = 'fixed';
include PQ . 'build/one.php'; // the walk: each entry, from/to selecting a slice of the list
build/types/bool.php)Tests each number with a boolean function (e.g., pqBoolPrime()):
$pq = ( 'pqBool' . ucfirst($pqSeq) ) ( $pqLoop, $pqParm );
build/types/function.php)Calls a custom function:
$pq = ( 'pq' . ucfirst($pqSeq) ) ( $pqLoop );
build/types/make.php)Generates the next value from current loop value (e.g., even numbers):
$pq = include PT . "$pqSeq/make.php";
build/types/order.php)Generates values that depend on previous values (e.g., Fibonacci):
$pq = include PT . "$pqSeq/order.php";
build/types/check.php)Answers whether a value is a term of a type - the build of keep, remove and flag. The
cheapest test the type has decides: its bool.php, the whole list its fixed.php or
build.php returns, its precomputed table, and failing those the terms are made and looked in:
$padSeqDefaultTries terms, and past a value
beyond the ceiling only while terms growing as they grew can still get to it;eval=, whose terms go wherever the expression says, $padSeqDefaultTries terms; a number
past every one of them is looked for in the terms at twice, four times … that many and at
the ceiling - one of them the value makes it a term, all of them short of it as the terms made
are leave it none, and otherwise every term to the ceiling is made. Terms that kept to one
direction over those first ones are taken to keep to it: an expression that turns back after
them is not followed there;chance and random as far as the value, which is drawn at its own position.A term past the ceiling is no term of the run: {sequence list='9223372036854775806', flag,
eval='@ * 2'} is 0, though @ * 2 makes it at position 4611686018427387903.
build/types/pull.php)Retrieves a previously stored sequence.
build/one.php)build/one.php is the walk itself, included once by the iterator: a loop over the
candidates, each settled in a do { ... } while ( FALSE ) block - break goes on to the
next candidate, break 2 ends the build. For each candidate:
$pqTries++;
if ( $pqTries > $pqTry ) break 2; // Max attempts reached
// Generate value based on build type
if ( pqStore($pqBuild) ) $pq = $pqLoop;
elseif ( $pqBuild == 'bool' ) $pq = pqBoolXxx($pqLoop, $pqParm);
elseif ( $pqBuild == 'function') $pq = pqXxx($pqLoop);
elseif ( $pqBuild == 'check' ) $pq = include PQ . "build/mode.php";
elseif ( $pqBuild == 'loop' ) $pq = include PT . "$pqSeq/loop.php";
elseif ( $pqBuild == 'make' ) $pq = include PT . "$pqSeq/make.php";
elseif ( $pqBuild == 'order' ) $pq = include PT . "$pqSeq/order.php";
// Apply filters (plays)
if ( count($pqPlays) ) {
include PQ . 'plays/plays.php';
if ( $pq === FALSE ) break; // Filtered out, next candidate
}
// Apply constraints
if ( $pq < $pqMin ) break; // Below minimum
if ( $pq > $pqMax ) break; // Above maximum
if ( $pqUnique and isset ( $pqUniqueSeen [pqValueKey($pq)] ) ) break; // Duplicate
// Add to result
$pqResult[] = $pq;
// Check stop conditions
if ( $pq >= $pqStop ) break 2;
if ( count($pqResult) >= $pqRows ) break 2;
It was a file the iterators included for every candidate, and an include costs what binding each variable the file names to the scope costs - some 320 ns a candidate for the 38 it names, most of what a candidate of a plain list took.
The 122 sequence types are organized in types/ with this structure:
types/
├── prime/
│ ├── bool.php # pqBoolPrime() - tests if n is prime
│ ├── function.php # pqPrime() - generates primes
│ ├── generated.php # PADprime constant with pre-computed values
│ ├── loop.php # Returns next prime >= n
│ ├── make.php # Finds next prime from n
│ └── flags/ # Type-specific flags
├── fibonacci/
│ ├── order.php # The order build: the seeds 0 and 1, then go.php
│ ├── go.php # The sum of the two terms before - shared with lucas
│ ├── generated.php # PADfibonacci constant
│ └── flags/
├── triangular/
│ ├── bool.php # Tests if n is triangular
│ ├── generated.php # PADtriangular constant
│ └── flags/
...
| File | Purpose |
|---|---|
bool.php |
Boolean test function pqBoolXxx($n) |
function.php |
Generator function pqXxx($n) |
generated.php |
Pre-computed constant array PADxxx |
loop.php |
Value generator for loop build |
make.php |
Value generator for make build |
order.php |
Order-dependent generator (uses previous values) |
go.php |
Alternative generator |
init.php |
Type initialization |
flags/ |
Type-specific configuration flags |
Arithmetic Operations:
add - Addition: n + parmsubtract - Subtraction: n - parmmultiply - Multiplication: n * parmdivide - Division: n / parmmodulo - Modulus: n % parmexponentiation - Power: n ^ parmnegation - Negation: -nBitwise Operations: (on the bits of the value, not on true and false)
and, or, xor, nand, nor, xnor, notNumber Properties:
even - Even numbersodd - Odd numbersprime - Prime numberscomposite - Composite numberssemiprime - Products of exactly two primesperfect - Perfect numbers (sum of divisors = n)palindrome - Palindromic numbershappy - Happy numbersharshad - Harshad/Niven numberskaprekar - Kaprekar numbersemirp - Primes that are different primes reversedstrong - Strong numbers / factorions (1, 2, 145, 40585), not strong primeslucky - Lucky numbers (1, 3, 7, 9, 13, 15, 21, 25, …)polite - Polite numberspowerful - Powerful numbersantiprime - Highly composite numbersFigurate Numbers:
triangular - 1, 3, 6, 10, 15…square - 1, 4, 9, 16, 25…pentagonal - 1, 5, 12, 22, 35…hexagonal - 1, 6, 15, 28, 45…heptagonal, octagonal, decagonal…gnomonic - 1, 3, 5, 7, 9…3D Figurate Numbers:
tetrahedral - Tetrahedral numbersoctahedral - Octahedral numberscubic - Cube numbersFamous Sequences:
fibonacci - 0, 1, 1, 2, 3, 5, 8…lucas - 1, 3, 4, 7, 11, 18… (starts at n = 1, so no leading 2)tribonacci - Tribonacci sequencepell - Pell numbers (1, 2, 5, 12, 29…)catalan - Catalan numbers (1, 2, 5, 14, 42…)bell - Bell numbers (1, 2, 5, 15, 52…)perrin - Perrin numbersxpadovan - Padovan sequencesylvester - Sylvester’s sequencerecaman - Recamán’s sequencegolomb - Golomb sequencekolakoski - Kolakoski sequencegould - Gould’s sequencenewmanConway - Newman-Conway sequencemoserdebruijn - Moser-de Bruijn sequenceSpecial:
mersenne - Mersenne primes, the 2^p - 1 that are prime (3, 7, 31, 127, 8191, …)cullen - Cullen numberskynea - Kynea numberscaterer - Lazy caterer’s sequencepronic - Pronic numbers (n*(n+1))biquadratic - Fourth powersUtility:
range - Simple range: from..tolist - Explicit list of valuesloop - Loop counterstep - Step sequencerepeat - Repeat valuerandom - Random numberschance - Random with probabilityidentity - Identity (returns input)eval - Evaluate expressionoeis - OEIS sequence lookupRounding:
ceil - Ceilingfloor - Floorround - Roundpower - Power of basemultiple - Multiple of valuePlays filter or transform values before adding to results (plays/plays.php):
foreach ( $pqPlays as $pqTmp ) {
extract ( $pqTmp );
$pqLoop = $pq;
$pq = include PQ . "plays/play/$pqBuild.php";
// Play modes:
if ( $pqPlay == 'make' ) // Transform value
if ( $pqPlay == 'keep' ) // Keep only matching
if ( $pqPlay == 'remove' ) // Remove matching
if ( $pqPlay == 'flag' ) // Return 1/0 flag
}
Play Modes:
| Mode | Returns TRUE | Returns FALSE | Returns other |
|---|---|---|---|
make |
Use loop value | Skip | Use returned value |
keep |
Keep value | Skip | Keep if matches loop |
remove |
Skip | Keep | Skip if matches loop |
flag |
Return 1 | Return 0 | 1 if matches, 0 otherwise |
Actions transform the final result array (actions/types/):
sum - Sum of all valuesproduct - Product of all valuesaverage - Average valuemedian - Median: values sorted, the middle one or the mean of the two middlesminimum - Minimum value, or the N smallest with minimum=Nmaximum - Maximum value, or the N largest with maximum=Ncount - Count of valuesfirst - First N valueslast - Last N valueselement - Specific elementslice - Slice of arraysplice - Splice arraypop - Take the last N out of the store and show themshift - Take the first N out of the store and show themappend - Add to endprepend - Add to startmerge - Merge arrayscombine - Combine arrayssort - Sort ascendingreverse - Reverse ordershuffle - Random orderrandomize - Draw N at random, all of them when barededup - Remove duplicatesdistinct - How many different values there are (a count)trim - Trim valuesonlyNow - Only current valuesonlyStore - Only stored valuesintersection - Common valuesdifference - Different valuesThe subsystem supports randomization at multiple levels:
Parameters can include ranges with ..:
from='1..100' # Random from 1-100
increment='1..5' # Random increment 1-5
rows='5..10' # Random 5-10 rows
With randomly option, values are selected randomly from the valid range instead of sequentially.
With increment='1...5' (three dots, quoted), the increment changes randomly each
iteration. Unquoted, 1...5 is read as the number 1.5 and the step is that fraction.
Many sequences include pre-computed constants in generated.php:
const PADfibonacci = [0,1,1,2,3,5,8,13,21,34,55,89,...];
const PADtriangular = [1,3,6,10,15,21,28,36,45,55,...];
const PADprime = [2,3,5,7,11,13,17,19,23,29,...];
These enable fast lookups for common sequences.
sequence/
├── sequence/
│ ├── tag.php # Entry point of a sequence tag
│ └── sequence.php # Entry point of sequence:<type>(n)
│
├── inits/ # Initialization
│ ├── clear.php # Clear state
│ ├── direct.php # Direct entry setup
│ ├── limits.php # Set iteration limits
│ ├── parms.php # Parse parameters
│ ├── set.php # Setup sequence type
│ └── vars.php # Initialize variables
│
├── build/ # Sequence generation
│ ├── build.php # Main build dispatcher
│ ├── check.php # Check build type
│ ├── given.php # Handle given build name
│ ├── include.php # Include type files
│ ├── one.php # Generate one value
│ ├── parm.php # Parameter handling
│ ├── randomly/ # Random support
│ └── types/ # Build type handlers
│ ├── bool.php
│ ├── build.php
│ ├── check.php
│ ├── fixed.php
│ ├── function.php
│ ├── given.php
│ ├── loop.php
│ ├── make.php
│ ├── order.php
│ ├── pull.php
│ └── type/ # Type-specific handlers
│
├── plays/ # Filter system
│ ├── add.php # Add play filter
│ ├── init.php # Initialize plays
│ ├── inits.php # Setup plays
│ ├── parm.php # Play parameters
│ ├── plays.php # Execute plays
│ └── play/ # Play type handlers
│
├── actions/ # Result transformations
│ ├── actions.php # Execute actions
│ ├── assoc.php # Associative handling
│ ├── function.php # Function actions
│ ├── inits.php # Setup actions
│ ├── merge.php # Merge handling
│ ├── set.php # Set actions
│ ├── double/ # Double-value actions
│ ├── merge/ # Merge actions
│ ├── negative/ # Negative handling
│ ├── order/ # Order actions
│ ├── parm/ # Parameterized actions
│ ├── single/ # Single-value actions
│ └── types/ # Action implementations
│
├── exits/ # Exit processing
│ ├── actions.php # Final actions
│ ├── data.php # Data storage
│ ├── done.php # Mark done
│ ├── exit.php # Exit handler
│ ├── exits.php # Exit processing
│ ├── info.php # Debug info
│ ├── return.php # Return handling
│ ├── extra/ # Extra processing
│ ├── info/ # Info generation
│ ├── return/ # Return handlers
│ └── store/ # Store handlers
│
├── options/ # Option handlers
│ └── types/ # Option types
│
└── types/ # 122 sequence types
├── add/
├── and/
├── antiprime/
├── bell/
... (122 types total)
{sequence '1..10'}
→ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
{sequence prime, rows=10}
→ [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
{sequence fibonacci, stop=100}
→ [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144]
stop ends the run at the first term that reaches it - that term included.
{sequence even, stop=20, sum}
→ [110]
For a type such as even, from and to count terms rather than name values:
{sequence even, from=2, to=20, sum} sums the 2nd to 20th even numbers, 4 to 40, and gives
[418]. stop always names a value.
{sequence prime, rows=20, keep, palindrome}
→ [2, 3, 5, 7, 11, 101, 131, 151, 181, 191...]
{sequence prime, rows=5, randomly}
→ [17, 3, 41, 7, 23] (random primes)
pqArray ( 'even', '', 'rows=3' ) is [ 2, 4, 6 ]: the terms of a type as the values they
are. The parameter is a value of the type - text in it is never template source - and the
options are the run’s named limits, from, to, sole, minimal, maximal, min, max,
increment, rows, try, stop, skip, randomly, unique and build, as an array
([ 'rows' => 3 ]) or written 'rows=3, to=50'. Plays and actions are the tag’s. An
expression for eval handed in from PHP is refused, as one from a value is: it is the
template’s own text.
Each sequence type can have a boolean test function:
function pqBoolPrime($n, $p=0) {
if (gmp_prob_prime($n))
return TRUE;
else
return FALSE;
}
function pqBoolEven($n, $p=0) {
if ($n & 1)
return FALSE;
else
return TRUE;
}
function pqBoolTriangular($num) {
// 0 and k(k+1)/2 for a whole k: k is about the square root of 2n, and the whole numbers
// next to it are tried exactly - solved as a quadratic in floating point, the neighbours
// of a triangular number past 10^16 came out triangular too
if ( ! pqBoolWhole ( $num ) or $num < 0 )
return false;
if ( $num == 0 )
return true;
$num = (int) $num;
$root = (int) sqrt ( 2 * (float) $num );
for ( $k = max ( 1, $root - 1 ); $k <= $root + 1; $k++ )
if ( ( ( $k % 2 ) ? $k * intdiv ( $k + 1, 2 ) : intdiv ( $k, 2 ) * ( $k + 1 ) ) == $num )
return true;
return false;
}
The functions in pad/sequence/types/<type>/bool.php are the ones that run.
Key state variables during generation:
| Variable | Purpose |
|---|---|
$pqResult |
Result array |
$pqLoop |
Current loop value |
$pqTries |
Number of attempts |
$pqSeq |
Current sequence type |
$pqBuild |
Current build type |
$pqParm |
Current parameter |
$pqOrder |
Order array (for dependent sequences) |
$pqPlays |
Active plays (filters) |
$pqActions |
Active actions |
$pqFixed |
Fixed array (for fixed builds) |