IntSpan(3)
π NAME π
okay so bestie, this is Set::IntSpan(3) β the manual for Set::IntSpan β and its whole main-character job is literally manages sets of integers. That's the serve. That's the vibe. π
π¦ SYNOPSIS π¦
bestie, this API is long but iconic. Take a breath, sip your iced coffee, and know that every single line below is real and exact. π«Ά
# BEGIN { $Set::IntSpan::integer = 1 }
use Set::IntSpan qw(grep_set map_set grep_spans map_spans);
# $Set::IntSpan::Empty_String = '-'; # or '';
$set = new Set::IntSpan $set_spec;
$set = new Set::IntSpan @set_specs;
$valid = valid Set::IntSpan $run_list;
$set = copy $set $set_spec;
$run_list = run_list $set;
@elements = elements $set;
@sets = sets $set;
@spans = spans $set;
$u_set = union $set $set_spec;
$i_set = intersect $set $set_spec;
$x_set = xor $set $set_spec;
$d_set = diff $set $set_spec;
$c_set = complement $set;
$set->U($set_spec); # Union
$set->I($set_spec); # Intersect
$set->X($set_spec); # Xor
$set->D($set_spec); # Diff
$set->C; # Complement
equal $set $set_spec
equivalent $set $set_spec
superset $set $set_spec
subset $set $set_spec
$n = cardinality $set;
$n = size $set;
empty $set
finite $set
neg_inf $set
pos_inf $set
infinite $set
universal $set
member $set $n;
insert $set $n;
remove $set $n;
$min = min $set;
$max = max $set;
$holes = holes $set;
$cover = cover $set;
$inset = inset $set $n;
$smaller = trim $set $n;
$bigger = pad $set $n;
$subset = grep_set { ... } $set;
$mapset = map_set { ... } $set;
$subset = grep_spans { ... } $set;
$mapset = map_spans { ... } $set;
for ($element=$set->first; defined $element; $element=$set->next) { ... }
for ($element=$set->last ; defined $element; $element=$set->prev) { ... }
$element = $set->start($n);
$element = $set->current;
$n = $set->at($i);
$slice = $set->slice($from, $to);
$i = $set->ord($n);
$i = $set->span_ord($n);
Operator overloads
$u_set = $set + $set_spec; # union
$i_set = $set * $set_spec; # intersect
$x_set = $set ^ $set_spec; # xor
$d_set = $set - $set_spec; # diff
$c_set = ~$set; # complement
$set += $set_spec; # union
$set *= $set_spec; # intersect
$set ^= $set_spec; # xor
$set -= $set_spec; # diff
$set eq $set_spec # equal
$set ne $set_spec # not equal
$set le $set_spec # subset
$set lt $set_spec # proper subset
$set ge $set_spec # superset
$set gt $set_spec # proper superset
# compare sets by cardinality
$set1 == $set2
$set1 != $set2
$set1 <= $set2
$set1 < $set2
$set1 >= $set2
$set1 > $set2
$set1 <=> $set2
# compare cardinality of set to an integer
$set1 == $n
$set1 != $n
$set1 <= $n
$set1 < $n
$set1 >= $n
$set1 > $n
$set1 <=> $n
@sorted = sort @sets; # sort sets by cardinality
if ($set) { ... } # true if $set is not empty
print "$set\n"; # stringizes to the run list
π EXPORTS π
@EXPORT
Nothing. literally nothing. zero. the void. bestie, it's giving mystery. π
@EXPORT_OK
These cuties are available if you ask for them: "grep_set", "map_set", "grep_spans", "map_spans".
π DESCRIPTION π
Set::IntSpan manages sets of integers. no cap. It is optimized for sets that have long runs of consecutive integers. These arise, for example, in .newsrc files, which maintain lists of articles:
alt.foo: 1-21,28,31
alt.bar: 1-14192,14194,14196-14221
A run of consecutive integers is also called a span. slay, we love a technical term with range.
Sets are stored internally in a run-length coded form. This provides for both compact storage and efficient computation. In particular, set operations can be performed directly on the encoded representation. it's giving big brain energy, fr fr.
Set::IntSpan is designed to manage finite sets. However, it can also represent some simple infinite sets, such as { x | x>n }. This allows operations involving complements to be carried out consistently, without having to worry about the actual value of INT_MAX on your machine. highkey iconic.
π SPANS π
A span is a run of consecutive integers. A span may be represented by an array reference, in any of 5 forms:
Finite forms
Span Set
[ $n, $n ] { n }
[ $a, $b ] { x | a<=x && x<=b}
Infinite forms
Span Set
[ undef, $b ] { x | x<=b }
[ $a , undef ] { x | x>=a }
[ undef, undef ] The set of all integers
Some methods operate directly on spans. iykyk. π¦
π SET SPECIFICATIONS π
Many of the methods take a set specification. There are four kinds of set specifications. Four! a whole little buffet. π
Empty
If a set specification is omitted, then the empty set is assumed. Thus,
$set = new Set::IntSpan;
creates a new, empty set. Similarly,
copy $set;
removes all elements from $set. not me cleaning house with one tiny call. π§Ή
Object reference
If an object reference is given, it is taken to be a Set::IntSpan object. simple. clean. no drama.
Run list
If a string is given, it is taken to be a run list. A run list specifies a set using a syntax similar to that in newsrc files.
A run list is a comma-separated list of runs. Each run specifies a set of consecutive integers. The set is the union of all the runs.
Runs may be written in any of 5 forms.
Finite forms
n { n }
a-b { x | a<=x && x<=b }
Infinite forms
(-n { x | x<=n }
n-) { x | x>=n }
(-) The set of all integers
Empty forms
The empty set is consistently written as '' (the null string). It is also denoted by the special form '-' (a single dash). bestie, yes, a single dash is carrying the whole empty-set look. π
Restrictions
π¨ vibe check: the runs in a run list must be disjoint, and must be listed in increasing order.
Valid characters in a run list are 0-9, '(', ')', '-' and ','. White space and underscore (_) are ignored. Other characters are not allowed. periodt.
Examples
Run list Set
"-" { }
"1" { 1 }
"1-2" { 1, 2 }
"-5--1" { -5, -4, -3, -2, -1 }
"(-)" the integers
"(--1" the negative integers
"1-3, 4, 18-21" { 1, 2, 3, 4, 18, 19, 20, 21 }
these examples ate, honestly. π₯
Array reference
If an array reference is given, then the elements of the array specify the elements of the set. The array may contain
- integers
- spans
The set is the union of all the integers and spans in the array. The integers and spans need not be disjoint. The integers and spans may be in any order.
Examples
Array ref Set
[ ] { }
[ 1, 1 ] { 1 }
[ 1, 3, 2 ] { 1, 2, 3 }
[ 1, [ 5, 8 ], 5, [ 7, 9 ], 2 ] { 1, 2, 5, 6, 7, 8, 9 }
[ undef, undef ] the integers
[ undef, -1 ] the negative integers
no cap, this is such a flexible little setup. πΈ
π§ ITERATORS π§
Each set has a single iterator, which is shared by all calls to first, last, start, next, prev, and current. At all times, the iterator is either an element of the set, or undef.
first, last, and start set the iterator; next, and prev move it; and current returns it. Calls to these methods may be freely intermixed.
Using next and prev, a single loop can move both forwards and backwards through a set. Using start, a loop can iterate over portions of an infinite set. the way that one iterator handles all that? lowkey obsessed. π«Ά
π METHODS π
Okay bestie, this is where we actually do stuff with the set. Every method is a little icon, and yes, we are listing them all β no skips, no "you can figure out the rest." π
β¨ Creation β¨
$set = "new" "Set::IntSpan" $set_specβ Creates and returns aSet::IntSpanobject. The initial contents of the set are given by$set_spec. New object, fresh vibes. π±$set = "new" "Set::IntSpan" @set_specsβ Also creates and returns aSet::IntSpanobject, but this time the initial contents are the union of all the@set_specs. Group project, but it actually works. π$ok = "valid" "Set::IntSpan" $run_listβ Returns true if$run_listis a valid run list. Otherwise it returns false and leaves an error message in$@. So check$@, bestie β it's giving diagnostics. π¨$set = "copy" $set $set_specβ Copies$set_specinto$set. The previous contents of$setare lost, okay? Gone. For convenience,copyreturns$set. π«Ά$run_list = "run_list" $setβ Returns a run list that represents$set. The run list will not contain white space.$setis not affected. By default, the empty set is formatted as'-'; a different string may be specified in$Set::IntSpan::Empty_String. Yes, that's a real variable name, and yes, it's iconic. π@elements = "elements" $setβ Returns an array containing the elements of$set. The elements will be sorted in numerical order. In scalar context, returns an array reference.$setis not affected. Sorted, untouched, and ready to spill. β¨@sets = "sets" $setβ Returns the runs in$set, as a list ofSet::IntSpanobjects. The sets in the list are in order. Cute little set family. π@spans = "spans" $setβ Returns the runs in$set, as a list of the form
([$a1, $b1],
[$a2, $b2],
...
[$aN, $bN])
If a run contains only a single integer, then the upper and lower bounds of the corresponding span will be equal. If the set has no lower bound, then $a1 will be undef. Similarly, if the set has no upper bound, then $bN will be undef. The runs in the list are in order. Undef boundaries are valid and not a glitch, bestie. π¦
π Set operations π
For these operations, a new Set::IntSpan object is created and returned. The operands are not affected. We are not out here mutating people without consent. π
ββοΈ
$u_set = "union" $set $set_specβ Returns the set of integers in either$setor$set_spec. Both? Either? It's all invited. π«Ά$i_set = "intersect" $set $set_specβ Returns the set of integers in both$setand$set_spec. Only the overlap gets in. π$x_set = "xor" $set $set_specβ Returns the set of integers in$setor$set_spec, but not both. Exclusive party, no plus-ones. π$d_set = "diff" $set $set_specβ Returns the set of integers in$setbut not in$set_spec. It's giving subtraction with attitude. β¨$c_set = "complement" $setβ Returns the set of integers that are not in$set. The whole universe minus your set. π
π¦ Mutators π¦
By popular demand, Set::IntSpan now has mutating forms of the binary set operations. These methods alter the object on which they are called. So yeah, glow-up in place. π₯
$set->"U"($set_spec)β Makes$setthe union of$setand$set_spec. Returns$set. π$set->"I"($set_spec)β Makes$setthe intersection of$setand$set_spec. Returns$set. π$set->"X"($set_spec)β Makes$setthe symmetric difference of$setand$set_spec. Returns$set. π$set->"D"($set_spec)β Makes$setthe difference of$setand$set_spec. Returns$set. βοΈ$set->"C"β Converts$setto its own complement. Returns$set. Full main-character transformation. π
πΈ Comparison πΈ
"equal" $set $set_specβ Returns true iff$setand$set_speccontain the same elements. Same ingredients, same set. π"equivalent" $set $set_specβ Returns true iff$setand$set_speccontain the same number of elements. All infinite sets are equivalent. Yes, all of them β infinity is infinity, no cap. βΎοΈ"superset" $set $set_specβ Returns true iff$setis a superset of$set_spec. Big set energy. π"subset" $set $set_specβ Returns true iff$setis a subset of$set_spec. Tiny set, big placement. π
π Cardinality π
$n = "cardinality" $setβ Returns the number of elements in$set. Returns-1for infinite sets. That's the "too many to count" signal, bestie. π’$n = "size" $setβ Provided as an alias forcardinality. Same number, shorter name. Slay. π"empty" $setβ Returns true iff$setis empty. Nothing in the bag. π"finite" $setβ Returns true iff$setis finite. Countable, contained, cute. β¨"neg_inf" $setβ Returns true iff$setcontains{x | x<n}for somen. Negative infinity vibes. π"pos_inf" $setβ Returns true iff$setcontains{x | x>n}for somen. Positive infinity vibes. π"infinite" $setβ Returns true iff$setis infinite. It goes on and on and on. βΎοΈ"universal" $setβ Returns true iff$setcontains all integers. Literally everything. Main character of sets. π
π Membership π
"member" $set $nβ Returns true iff the integer$nis a member of$set. Are you in the group chat or not? π¬"insert" $set $nβ Inserts the integer$ninto$set. Does nothing if$nis already a member of$set. No duplicate spam. π ββοΈ"remove" $set $nβ Removes the integer$nfrom$set. Does nothing if$nis not a member of$set. Peace out, but only if you were actually here. π
π₯ Extrema π₯
"min" $setβ Returns the smallest element of$set, orundefif there is none. Baby of the set. πΌ"max" $setβ Returns the largest element of$set, orundefif there is none. Big boss of the set. π
π§ Spans π§
$holes = "holes" $setβ Returns a set containing all the holes in$set, that is, all the integers that are in-between spans of$set.holesis always a finite set. Even if the set is infinite, the holes are finite β that's the rule. π³οΈ$cover = "cover" $setβ Returns a set consisting of a single span from$set->"min"to$set->"max". This is the same as
union $set $set->holes
One span to cover them all. π‘οΈ
$inset = "inset" $set $n/$smaller = "trim" $set $n/$bigger = "pad" $set $nβinsetreturns a set constructed by removing$nintegers from each end of each span of$set. If$nis negative, then-$nintegers are added to each end of each span. In the first case, spans may vanish from the set; in the second case, holes may vanish.trimis provided as a synonym forinset.pad $set $nis the same asinset $set -$n. Shrinking and padding, both valid, both dramatic. π
π Iterators π
$set->"first"β Sets the iterator for$setto the smallest element of$set. If there is no smallest element, sets the iterator toundef. Returns the iterator. Starting at the beginning, bestie. π¬$set->"last"β Sets the iterator for$setto the largest element of$set. If there is no largest element, sets the iterator toundef. Returns the iterator. Ending at the end. ποΈ$set->"start"($n)β Sets the iterator for$setto$n. If$nis not an element of$set, sets the iterator toundef. Returns the iterator. Choose your fighter. π₯$set->"next"β Sets the iterator for$setto the next element of$set. If there is no next element, sets the iterator toundef. Returns the iterator.nextwill returnundefonly once; the next call tonextwill reset the iterator to the smallest element of$set. Loop-de-loop. π$set->"prev"β Sets the iterator for$setto the previous element of$set. If there is no previous element, sets the iterator toundef. Returns the iterator.prevwill returnundefonly once; the next call toprevwill reset the iterator to the largest element of$set. Backwards but make it fashion. π$set->"current"β Returns the iterator for$set. Where are we right now? π
π Indexing π
The elements of a set are kept in numerical order. These methods index into the set based on this ordering. Order is the whole point, okay? β¨
$n = $set->"at"($i)β Returns the$ith element of$set, orundefif there is no$ith element. Negative indices count backwards from the end of the set. Dies if$iis non-negative and$setisneg_inf$iis negative and$setispos_inf
So yes, it can absolutely die on you β respect the boundaries. π
$slice = $set->"slice"($from, $to)β Returns aSet::IntSpanobject containing the elements of$setat indices$from..$to. Negative indices count backwards from the end of the set. Dies if$fromis non-negative and$setisneg_inf$fromis negative and$setispos_inf
Slice it, but know the danger. πͺ
$i = $set->"ord"($n)β The inverse ofat. Returns the index$iof the integer$nin$set, orundefif$nisn't an element of$set. Dies if$setisneg_inf. Reverse lookup, but make it lethal. π«$i = $set->"span_ord"($n)β Returns the index$iof the span containing the integer$n, orundefif$nisn't an element of$set. To recover the span containing$n, write
($set->spans)[$i]
Span detective work, unlocked. π
π OPERATOR OVERLOADS π
For convenience, some operators are overloaded on Set::IntSpan objects. Yes, the set is doing math AND string comparisons. Multitalented. π
π₯ set operations π₯
One operand must be a Set::IntSpan object. The other operand may be a Set::IntSpan object or a set specification.
$u_set = $set + $set_spec; # union
$i_set = $set * $set_spec; # intersect
$x_set = $set ^ $set_spec; # xor
$d_set = $set - $set_spec; # diff
$c_set = ~$set; # complement
$set += $set_spec; # union
$set *= $set_spec; # intersect
$set ^= $set_spec; # xor
$set -= $set_spec; # diff
All the operators, all the comments, exactly where they belong. π«Ά
π equality π
The string comparison operations are overloaded to compare sets for equality and containment. One operand must be a Set::IntSpan object. The other operand may be a Set::IntSpan object or a set specification.
$set eq $set_spec # equal
$set ne $set_spec # not equal
$set le $set_spec # subset
$set lt $set_spec # proper subset
$set ge $set_spec # superset
$set gt $set_spec # proper superset
String operators but set logic. It's giving crossover episode. β¨
π equivalence π
The numerical comparison operations are overloaded to compare sets by cardinality. One operand must be a Set::IntSpan object. The other operand may be a Set::IntSpan object or an integer.
$set1 == $set2
$set1 != $set2
$set1 <= $set2
$set1 < $set2
$set1 >= $set2
$set1 > $set2
$set1 <=> $set2
$set1 cmp $set2
$set1 == $n
$set1 != $n
$set1 <= $n
$set1 < $n
$set1 >= $n
$set1 > $n
$set1 <=> $n
$set1 cmp $n
N.B. The cmp operator is overloaded to compare sets by cardinality, not containment. This is done so
sort @sets
will sort a list of sets by cardinality. Yes, cmp is doing cardinality duty on purpose. No, it is not a containment check. Don't get it twisted. π
π± conversion π±
In boolean context, a Set::IntSpan object evaluates to true if it is not empty. Empty set? False. Not empty? True. That's the vibe check. β
A Set::IntSpan object stringizes to its run list. So when it has to be a string, it's a run list. Periodt. π
β¨ FUNCTIONS β¨
$sub_set = "grep_set" { ... } $setβ okay so this one evaluates the BLOCK for each integer in$set, locally setting$_to each integer, and returns aSet::IntSpanobject containing those integers for which the BLOCK returns TRUE. If$setis infinite, you getundefback. No cap. π$map_set = "map_set" { ... } $setβ same group-chat energy, but this one collects results: it evaluates the BLOCK for each integer in$set(again locally setting$_to each integer) and returns aSet::IntSpanobject containing all the integers returned as results of all those evaluations. It evaluates the BLOCK in list context, so each element of$setmay produce zero, one, or more elements in the returned set. The elements may be returned in any order, and need not be disjoint. Returnsundefif$setis infinite. Periodt. π«Ά$sub_set = "grep_spans" { ... } $setβ this is the span-level filter, bestie: it evaluates the BLOCK for each span in$setand returns aSet::IntSpanobject containing those spans for which the BLOCK returns TRUE. Within BLOCK,$_is locally set to an array ref of the form[ $lower, $upper ], where$lowerand$upperare the bounds of the span. If the span contains only one integer, then$lowerand$upperwill be equal. If the span is unbounded, then the corresponding element(s) of the array will beundef. Iconic. πΈ$map_set = "map_spans" { ... } $setβ this one evaluates the BLOCK for each span in$set, and returns aSet::IntSpanobject consisting of the union of all the spans returned as results of all those evaluations. Within BLOCK,$_is locally set to an array ref of the form[ $lower, $upper ]as described above forgrep_spans. Each evaluation of BLOCK must return a list of spans. Each returned list may contain zero, one, or more spans. Spans may be returned in any order, and need not be disjoint. However, for each bounded span, the constraint$lower <= $uppermust hold. Like, that part is non-negotiable. π
π CLASS VARIABLES π
$Set::IntSpan::Empty_String β this contains the string that is returned when run_list is called on the empty set. $Empty_String is initially '-'; alternatively, it may be set to ''. Other values should be avoided, to ensure that run_list always returns a valid run list. β¨
run_list accesses $Empty_String through a reference stored in $set->{"empty_string"}. Subclasses that wish to override the value of $Empty_String can reassign this reference. Slay.
$Set::IntSpan::integer β up until version 1.16, Set::IntSpan specified use integer, because they were sets ofβ¦you knowβ¦integers. As of 2012, users are reporting newsgroups with article numbers above 0x7fffffff, which break Set::IntSpan on 32-bit processors. π
Version 1.17 removes use integer by default. This extends the usable range of Set::IntSpan to the number of bits in the mantissa of your floating point representation. For IEEE 754 doubles, this is 53 bits, or around 9e15. Big numbers, big vibes. π₯
The author benchmarked Set::IntSpan on a Pentium 4, and it looks like use integer provides a 2% to 4% speedup, depending on the application.
If you want use integer back, either for performance, or because you are somehow dependent on its semantics, write:
BEGIN { $Set::IntSpan::integer = 1 }
use Set::IntSpan;
π DIAGNOSTICS π
Any method (except valid) will die if it is passed an invalid run list. Yeah, she does not play about invalid run lists. π
Set::IntSpan::_copy_run_list: Bad syntax:$runListβ (F)$run_listhas bad syntax.Set::IntSpan::_copy_run_list: Bad order:$runListβ (F)$run_listhas overlapping runs or runs that are out of order.Set::IntSpan::elements: infinite setβ (F) An infinite set was passed toelements.Set::IntSpan::at: negative infinite setβ (F)atwas called with a non-negative index on a negative infinite set.Set::IntSpan::at: positive infinite setβ (F)atwas called with a negative index on a positive infinite set.Set::IntSpan::slice: negative infinite setβ (F)slicewas called with$fromnon-negative on a negative infinite set.Set::IntSpan::slice: positive infinite setβ (F)slicewas called with$fromnegative on a positive infinite set.Set::IntSpan::ord: negative infinite setβ (F)ordwas called on a negative infinite set.Out of memory!β (X)elements$setcan generate anOut of memory!message on sufficiently large finite sets.
π NOTES π
π Traps π
Beware of forms like:
union $set [1..5];
This passes an element of @set to union, which is probably not what you want. To force interpretation of $set and [1..5] as separate arguments, use forms like:
union $set +[1..5];
or:
$set->union([1..5]);
bestie, this is the kind of tiny syntax thing that will have you staring at your terminal like π β so yeah, watch out for it.
π¦ grep_set and map_set π¦
grep_set and map_set make it easy to construct sets for which the internal representation used by Set::IntSpan is not small. Consider:
$billion = new Set::IntSpan '0-1_000_000_000'; # OK
$odd = grep_set { $_ & 1 } $billion; # trouble
$even = map_set { $_ * 2 } $billion; # double trouble
Likeβ¦the first one is fine, the second one is giving trouble, and the third one is double trouble. Ngl, that comment section ate. π
π Error handling π
There are two common approaches to error handling: exceptions and return codes. There seems to be some religion on the topic, so Set::IntSpan provides support for both. Inclusive king behavior. π
To catch exceptions, protect method calls with an eval:
$run_list = <STDIN>;
eval { $set = new Set::IntSpan $run_list };
$@ and print "$@: try again\n";
To check return codes, use an appropriate method call to validate arguments:
$run_list = <STDIN>;
if (valid Set::IntSpan $run_list)
{ $set = new Set::IntSpan $run_list }
else
{ print "$@ try again\n" }
Similarly, use finite to protect calls to elements:
finite $set and @elements = elements $set;
Calling elements on a large, finite set can generate an Out of memory! message, which cannot (easily) be trapped. Applications that must retain control after an error can use intersect to protect calls to elements:
@elements = elements { intersect $set "-1_000_000 - 1_000_000" };
or check the size of $set first:
finite $set and cardinality $set < 2_000_000 and @elements = elements $set;
π Limitations π
Although Set::IntSpan can represent some infinite sets, it does not perform infinite-precision arithmetic. Therefore, finite elements are restricted to the range of integers on your machine. So likeβ¦infinite-set representation is a vibe, but it is not magic. β¨
π§ Extensions π§
Users report that you can construct Set::IntSpan objects on anything that behaves like an integer. For example:
$x = new Math::BigInt ...;
$set = new Set::Intspan [ [ $x, $x+5 ] ];
The author is not documenting this as supported behavior, because there are not enough resources to test it, but promises to try not to break it. If anyone finds problems with it, let them know. π«Ά
πΈ Roots πΈ
The sets implemented here are based on a Macintosh data structure called a region. See Inside Macintosh for more information.
Set::IntSpan was originally written to manage run lists for the News::Newsrc module. Historical icon behavior. π
π AUTHOR π
Steven McDougall <swmcd@world.std.com>
π ACKNOWLEDGMENTS π
- Malcolm Cook
<mec@stowers-institute.org> - David Hawthorne
<dsrthorne@hotmail.com> - Martin Krzywinski
<martink@bcgsc.ca> - Marc Lehmann
<schmorp@schmorp.de> - Andrew Olson
<aolson@me.com> - Nicholas Redgrave
<baron@bologrew.net>
β COPYRIGHT β
Copyright (c) 1996-2013 by Steven McDougall. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
Thatβs the whole page, bestie! π«Ά Source/version vibes: perl v5.42.0, dated 2013-09-09, and the page is IntSpan(3) β bye, love you, read your manual pages π
π
π¦ where this page comes from
- opensuse-leap 16.0: perl-Set-IntSpan 1.19-bp160.1.11 Β· GPL-1.0+ or Artistic-1.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms