3
1
Fork
You've already forked postscript-libraries
1
Utility functions for the PostScript programming language
  • PostScript 99.4%
  • Shell 0.6%
2026年06月29日 17:34:38 +01:00
combinatorics.ps Move permute, combinations and cartesianproduct into new combinatorics file. 2023年05月14日 18:16:34 +01:00
combinatorics_test.ps Move permute, combinations and cartesianproduct into new combinatorics file. 2023年05月14日 18:16:34 +01:00
counter.ps extend counter functionality 2026年06月29日 17:34:38 +01:00
counter_test.ps extend counter functionality 2026年06月29日 17:34:38 +01:00
date.ps Remove juliandate 2022年06月08日 22:12:36 +01:00
date_test.ps Remove juliandate 2022年06月08日 22:12:36 +01:00
deepeq.ps Fix logic error in deepeq dict mode that was leaking onto the stack. 2025年11月30日 14:59:00 +00:00
deepeq_test.ps add deepcopy to deepeq 2023年04月24日 13:28:40 +01:00
file-test-file Add file sublibrary 2022年12月01日 12:25:25 +00:00
file.ps Add file sublibrary 2022年12月01日 12:25:25 +00:00
file_test.ps Add file sublibrary 2022年12月01日 12:25:25 +00:00
iterables.ps add minmax to iterables 2025年09月03日 15:00:31 +01:00
iterables_test.ps add minmax to iterables 2025年09月03日 15:00:31 +01:00
LICENCE Licence 2022年05月13日 16:05:51 +01:00
maths.ps amend sqrt-cvi to use isqrt 2023年10月24日 12:17:41 +01:00
maths_test.ps Add integer square root 2023年10月24日 12:16:33 +01:00
mergesort.ps Merge sort 2023年10月16日 11:26:28 +01:00
mergesort_test.ps Merge sort 2023年10月16日 11:26:28 +01:00
quicksort.ps Add custom comparator and key sorting functions 2023年05月14日 18:10:13 +01:00
quicksort_test.ps Add custom comparator and key sorting functions 2023年05月14日 18:10:13 +01:00
README.md extend counter functionality 2026年06月29日 17:34:38 +01:00
runps Initial upload 2022年05月13日 15:51:19 +01:00
strings.ps Add POSIX character class tests 2024年04月28日 16:56:02 +01:00
strings_test.ps Add POSIX character class tests 2024年04月28日 16:56:02 +01:00
test.ps Add detection of stack leakage 2025年11月30日 16:20:13 +00:00
test_test.ps Initial upload 2022年05月13日 15:51:19 +01:00

Postscript Libraries

These library functions add some modern functionality to the admirably lightweight PostScript language.

There are two ways to use them: with the run function, or with direct inclusion. The former is easier with GhostScript, though you'll need to disable the SAFER option, e.g.:

gs -q -dNOSAFER -sDEVICE=txtwrite -o - filename.ps

or using the runps shell script included. Or take each function (and its dependencies) and copy it into your single program file; I'm working on a helper program to do this automatically.

I'm licencing these under GPL v3, but if you do actually get any use out of them, or want any functionality added, I'd love to hear about it.

The Libraries

combinatorics

  • permute

Permutes an array in place (using Heap's algorithm), applying a procedure to each permutation. Note that this changes the contents of the array, rather than building a new one each time.

[ 1 2 3 ] { [ exch aload pop ] } permute → [1 2 3] [2 1 3] [3 1 2]
 [1 3 2] [2 3 1] [3 2 1]
  • combinations

Given an array and a number, return an aray of all combinations of that size (in Knuth's lexicographic order).

[ 1 2 3 ] 2 combinations → [ [ 2 1 ] [ 3 1 ] [ 3 2 ] ]
  • cartesianproduct

Returns an array of arrays, the n-ary cartesian product of the array of input arrays. So for example an input of

[ [ 1 2 ] [ 3 4 ] ]

would produce

[ [ 1 3 ] [ 1 4 ] [ 2 3 ] [ 2 4 ] ]

deepeq

  • deepeq

Returns true if the two values on the top of the stack are equal – recursively checking strings, arrays and dictionaries for matching values.

[ 1 2 [ 3 4 ] ] [ 1 2 [ 3 4 ] ] deepeq → true
  • deepcopy

Makes a deep (dereferencing) copy of the data structure - values will be identical, but the two structures have no references in common, so that one can be changed without affecting the other.

date

Various date-related calculations. A "ymd" is an array of [ year month day ] values; a "jd" is the integer value of the Julian date at noon GMT of that day.

  • ymd2jd

Converts a year-month-day tuple to a julian date (integer value at noon GMT on that day).

[ 2013 1 1 ] ymd2jd → 2456294
  • jd2ymd

Converts a julian date to year-month-day.

2456294 jd2ymd → [ 2013 1 1 ]
  • jd2dow

Extracts the day of the week from a Julian date (0 = Sunday).

[ 2022 6 13 ] ymd2jd jd2dow → 1
  • lastdayofyear

Calculates the last day of the year (0 = Sunday)

2009 lastdayofyear → 4
  • islongyear

Returns true for an ISO8601 long year.

2009 islongyear → true
  • yearweeks

Returns the number of ISO8601 weeks in the year. (52, 53 if islongyear.)

2009 yearweeks → 53
  • ymd2isoywd

Converts a year-month-day tuple to an ISO8601 year-week-day tuple.

 [ 1978 1 1 ] ymd2isoywd → [ 1977 52 7 ]
  • y2easter

Given a year, returns the year-month-day tuple for Gregorian Easter.

2022 y2easter → [ 2022 4 17 ]

iterables

This is basically for treating arrays as lists, and mangling arrays into dicts and back.

  • keys

Given a dict, return an array of the keys.

<< 1 (a) 2 (b) 3 (c) 4 (d) >> keys → [ 1 2 3 4 ]
  • values

Given a dict, return an array of the values. (This will probably be in the same order as keys.)

<< 1 (a) 2 (b) 3 (c) 4 (d) >> values → [ (a) (b) (c) (d) ]
  • toset

Given an array, deduplicate it and return a dict of (array-value → true) pairs; the opposite of keys.

[ 1 2 3 4 ] toset → << 1 true 2 true 3 true 4 true >>
  • set.union, set.intersection, set.difference

Perform Boolean operations on sets (or any dicts, ignoring values).

[ 1 2 3 ] toset [ 3 4 5 ] toset set.union → [ 1 2 3 4 5 ] toset
[ 1 2 3 ] toset [ 3 4 5 ] toset set.intersection → [ 3 ] toset
[ 1 2 3 ] toset [ 3 4 5 ] toset set.difference → [ 1 2 ] toset
  • map

Given an array and a procedure, apply the procedure to each array element in turn, returning an array of the output.

[ 1 2 3 ] { 1 add } map → [ 2 3 4 ]
  • filter

Given an array and a procedure that returns a boolean, output an array consisting only of the values that cause a true output.

[ 1 2 3 4 5 ] { 2 mod 0 eq } → [ 2 4 ]
  • reduce

Given an array and a procedure that takes two inputs and leaves one on the stack, apply it repeatedly to reduce the array to a single value.

[ 1 2 3 4 5 ] { add } reduce → 15
  • reduce_init

As above, but with an initial value for the accumulator.

0 [ 1 2 4 5 ] { 1 exch div add } reduce_init → 1.95
  • enumerate

Given an array, produce an array twice the length, consisting of pairs of index and array value.

[ (a) (b) (c) ] enumerate → [ 0 (a) 1 (b) 2 (c) ]
  • enumerate.array

Given an array, produce an array twice the length, consisting of array pairs of index and array value.

[ (a) (b) (c) ] enumerate.array → [ [ 0 (a) ] [ 1 (b) ] [ 2 (c) ] ]

This is probably the more useful form. Where Rust might say

for (i, n) in s.iter().enumerate() { ...

the equivalent here is

s enumerate.array {
 aload pop
 /n exch def
 /i exch def
 ...
} forall
  • apush.right

Create a new array consisting of the input array and a new value. apush is an alias.

[ 1 2 ] 3 apush.right → [ 1 2 3 ]
  • apush.left

As above but inserts a new initial value.

[ 2 3 ] 1 apush.left → [ 1 2 3 ]
  • apop.left

Create a new array consisting of the input array without its initial value, and that value separately.

[ 1 2 3 ] apop.left → [ 2 3 ] 1
  • apop.right

As above but for the final value. apop is an alias.

[ 1 2 3 ] apop.right → [ 1 2 ] 3
  • all

Given an array and a function, return true if all of the values of the function applied to a list member are true, otherwise false.

[ 1 2 3 4 ] { 0 gt } all → true
[ 1 2 3 4 ] { 1 eq } all → false
  • any

Given an array and a function, return true if any of the values of the function applied to a list member are true, otherwise false.

[ 1 2 3 4 ] { 0 eq } any → false
[ 1 2 3 4 ] { 1 eq } any → true
  • listmax, listmin

Given an array, return the maximum or minimum value in it. (This uses the non-standard two-argument max and min extensions in GhostScript.)

[ 6 4 4 5 2 4 ] listmax → 6
[ 4 3 3 2 3 6 ] listmin → 2
  • minmax

Calculate both minimum and maximum values of a list, with minimal comparisons.

[ 1 2 3 4 5 ] minmax → [ 1 5 ]
  • unique

Given an array which may have duplicate elements, return an array with no duplicates. (In arbitrary order.)

[ 1 1 2 3 4 4 4 ] unique quicksort → [ 1 2 3 4 ]
  • reverse

Given an array, returns that array reversed.

[ 1 2 3 4 ] reverse → [ 4 3 2 1 ]
  • remove

Given an array and a position, return a copy of the array with the element at that position removed.

[ 0 1 2 3 4 ] 2 remove → [ 0 1 3 4 ]
  • dget

Given a dict, key and default, return the dict's value at key if it exists, or the default if not.

<< 1 2 2 4 3 8 4 16 >> 5 0 dget → 0
  • rotor

Inspired by Raku's rotor, this will produce sublists of a list with or without overlap. Incomplete sublists at the end are not produced.

[ 0 1 2 3 ] 2 0 rotor → [ [ 0 1 ] [ 2 3 ] ]
[ 0 1 2 3 ] 2 -1 rotor → [ [ 0 1 ] [ 1 2 ] [ 2 3 ] ]

counter

These functions are for use on what I call "counter dicts". Each entry has an item as the key, and a count of instances of that item as the value, like Raku's Bag class or "multisets" in other languages. The functions are inspired by the counter class for Rust, which in turn is inspired by Python3's Counter.

Since we have no type safety, there's nothing to stop you using these functions on other sorts of dict. Don't do that.

  • tocounter

Convert an array to a counter.

[ 7 10 7 2 3 9 8 10 7 10 ] tocounter →
<< 9 1 2 1 8 1 10 3 3 1 7 3 >>
  • counter.most_common_ordered

Return an array of arrays: each element contains one item and its frequency, highest frequencies first, ties broken by sortably earliest item first.

<< 9 1 2 1 8 1 10 3 3 1 7 3 >> counter.most_common_ordered →
[ [ 7 3 ] [ 10 3 ] [ 2 1 ] [ 3 1 ] [ 8 1 ] [ 9 1 ] ] 
  • counter.most_common_tiebreaker

As above, but supply your own tiebreaker function. This is defined as for quicksort.

<< 9 1 2 1 8 1 10 3 3 1 7 3 >>
{ quicksort.cmp neg }
counter.most_common_tiebreaker →
[ [ 10 3 ] [ 7 3 ] [ 9 1 ] [ 8 1 ] [ 3 1 ] [ 2 1 ] ]
  • counter.is_subset

Return true if all the items in the first counter are also present in the second, with a value at least as high.

  • counter.is_superset

Return true if all the items in the second counter are also present in the first, with a value at least as high.

  • counter.intersection

Return the intersection of the two counters: the items that occur in both of them, with the lower value.

  • counter.union

Return the union of the two counters: the items that occur in either of them, with the higher value if both exist.

  • counter.add

Return the sum of the two counters: the items that occur in either of them, with the values added together if both exist. (This is more or less the extend method from Rust Counter.)

  • counter.sub

Return the difference of the two counters: the items that occur in either of them, with the values in the second counter subtracted from those in the first if both exist. (These can become zero or negative.)

  • counter.elements

Return an array which would produce the given counter if it were fed to tocounter. (No guarantee of ordering!)

<< 65 3 66 2 >> counter.elements → [ 65 65 65 66 66]
  • counter.total

Return the sum of all values in the counter.

maths

  • genprimes

Generate an (unsorted) list of prime numbers less than or equal to the parameter. (Often you'll then want to quicksort them.)

20 genprimes quicksort → [ 2 3 5 7 11 13 17 19 ]
  • isprime

Return a boolean value indicating whether the integer argument is prime.

67 isprime → true
  • primefactor

Generate a dict of prime factors and their powers.

75 primefactor → << 3 1 5 2 >>
  • nthprimelimit

Calculate a number which is guaranteed to be no lower than the nth prime. This uses the algorithm of Barkley Rosser as given in Explicit Bounds for Some Functions of Prime Numbers, American Journal of Mathematics, Vol. 63, No. 1 (Jan., 1941), pp. 211-232; the output will typically be fed to genprimes.

25 nthprimelimit → 110
  • gcd

Calculate the greatest common divisor (highest common factor) of two numbers.

8 12 gcd → 4
  • lcm

Calculate the lowest common multiple of two numbers.

4 6 lcm → 12
  • isqrt

Calculate the integer square root of its argument.

19 isqrt → 4

quicksort

  • quicksort

Sorts an array in place.

 [ 4 9 4 7 ] quicksort → [ 4 4 7 9 ]
  • quicksort.with_comparator

Sorts an array with a custom comparison function. This should return, for stack values A B, -1 if A < B, 0 if A == B, 1 if A > B. The quicksort.cmp function will do this evaluation with standard gt/lt tests.

Strings normally sort in ASCIIbetical order:

 [ (4) (10) (4) (7) ] quicksort → [ (10) (4) (4) (7) ]

A custom comparator compares the strings' integer values:

 [ (4) (10) (4) (7) ] {
 cvi exch cvi exch quicksort.cmp
 } quicksort.with_comparator →
 [ (4) (4) (7) (10) ]
  • quicksort.with_keygen

Sorts an array with a key generation function which will be applied to each element. (Calculated just once for each, and cached.)

[ (distressfulness) (this) (fungus) ] { length }
quicksort.with_keygen →
[ (this) (fungus) (distressfulness)]
  • quicksort.with_keylist

Sorts an array with a key list.

[ (a) (b) (c) ] [ 3 1 2 ] quicksort.with_keylist → [ (b) (c) (a) ]

mergesort

As with quicksort methods above, but using a stable merge sort.

(Relies on cmp being undefined - I'll fix that in a future version.)

strings

  • strconcat

Concatenates two strings.

(abc) (def) strconcat (abcdef)
  • strjoin

Joins an array of strings with a joining string.

[ (abc) (def) ] (, ) strjoin (abc, def)
  • strsplit

Breaks down a string into an array of strings (eliding any zero-length output).

(this and that and the other) ( and ) strsplit
→ [ (this) (that) (the other) ]
  • s2a

Converts a string to an array of character values.

(abcdef) s2a → [97 98 99 100 101 102]
  • a2s

Converts an array of character values to a string.

[97 98 99 100 101 102] a2s → (abcdef)
  • safestring

Replaces the string on top of the stack with an unlinked copy, so that you can change the copy without changing the original.

(abcdef) safestring → (abcdef)
  • alloccvs

Like cvs, but allocates a string of appropriate length. Works on positive and negative integers.

555 alloccvs → (555)
  • toupper/tolower

Returns a string with all lower-case characters shifted to upper, or vice versa.

(UPPERlower) tolower → (upperlower)

  • POSIX character classes

Each function is named "c.is" plus the character class: c.isalnum, c.isalpha, etc., and returns a boolean.

These functions all operate on individual characters, not strings. To operate on strings, combine with the iterables library. For example, to verify that every character in a string is upper case:

(string) s2a { c.isupper } all

To extract only the digits from a string and return them as a new string:

(string) s2a { c.isdigit } filter a2s

  • alnum = alpha | digit
  • alpha = "A"-"Z" | "a"-"z"
  • blank = 9 | 32
  • cntrl = 0-31 | 127
  • digit = 48-57
  • graph = alnum | punct
  • lower = "a"-"z"
  • print = alnum | punct | 32
  • punct = ^alnum & ^cntrl & ^32
  • space = tab | nl | vt | ff | cr | 32
  • upper = "A"-"Z"
  • xdigit = digit | "A"-"F" | "a"-"f"

file

  • readlines

Given a text filename, returns an array of strings, one per line in the file.

test

Start a series of tests with (string) test.start.

Build tests to output booleans, and run test on each.

End the series with test.end.

Example:

(Boolean) test.start
true test
false not test
test.end

Output:

Boolean: Passed 2/2 (100%)