Perl 6 Numerics
Solomon Foster <colomon@gmail.com>
#perl6
Perl 6 Numerics
 Powerful
 Takes advantage of both classes and roles
 Extensible
 "There's more than one way to do it"
Numeric Roles
 The two core roles are
Numeric
and Real
 Numeric represents any scalar number
 Real represents any numeric type which is a subset of the real numbers
 Examples include
Int
, Num
, and Rat

Real
does Numeric
(ie all Real
s are also Numeric
)
What is Numeric but not Real?
 Complex numbers are the canonical example
 Util has implemented Quaternions in a module
What math types aren't Numeric?
 Math types which do not have a solid definition for the basic operators
 For instance, a 3D vector type probably isn't
Numeric
 It has addition and subtraction
 It doesn't have multiplication or division with other vectors
 These boundaries are still a bit fuzzy
So, Real first
 If you want to handle things on the real number line
 And you don't care about the particular type
 The
Real
role provides a full suite of standard math operations
 Arithmetic operators:

(prefix) +

*
/
%
**
 Comparison operators:
cmp
<=>
==
!=
<
>
<=
>=
Real Methods
 Basic methods:
abs
, exp
, log
, sqrt
, roots
, sign
, floor
, ceiling
, truncate
, round
, cis
, unpolar
, rand
 Trig methods:
sin
, asin
, cos
, acos
, tan
, atan
, sec
, asec
, cosec
, acosec
, cotan
, acotan
, atan2
 Hyperbolic trig methods:
sinh
, asinh
, cosh
, acosh
, tanh
, atanh
, sech
, asech
, cosech
, acosech
, cotanh
, acotanh
 Coercion and test methods:
Real
, Bool
, Int
, Rat
, Num
, Complex
, Str
, Bridge
, reals
, isNaN
Gotchas
 These all work for every
Real
type
 But their result may be a different
Real
type
 Consider
10.sqrt

10
is an Int
 The result (
3.16227766016838
) is a Num
 That's true for
9.sqrt
as well, even though 3
could be an Int
Also...
 You might lose precision this way

(10 ** 1000).sqrt
is not guaranteed to be exactly 10 ** 500
 This is underspecified and may change
Num
 Your basic machine floatingpoint double
 Boring and useful
Int
 Infinite precision integer type
 Big numbers NYI in Rakudo, work in Niecza
 Includes
div
, mod
, gcd
, and lcm
operators
 If you use
/
on two Int
s, the result is a Rat
 In theory, also has values
Inf
and +Inf
int
 Spec defines
int
and various intNNN
types
 Native integers (NNN bits)
 NYI anywhere, maybe in the next generation of Rakudo
Rat
 Perl 6's default
Rational
type
 If possible, a
Rat
is constructed when you use Int / Int
 If possible?
 By spec, numerator is an
Int
, denominator an uint64
 The denominator is finite to avoid situations where it explodes in size
 So if your
Int / Int
cannot fit it in the representation, it must create something else
Rat II
 Degrades to either
Num
or a lesser precision Rat
 In practice, both Rakudo and Niecza choose
Num
 I'd kind of like to change the spec to match that
 If you want extended precision rationals, you need to ask for a
FatRat
explicitly
 In practice in Rakudo,
Rat
is (finite) Int
over (finite) Int
Rat III
 The default format for literal decimal numbers in Perl 6
 That is to say,
9.45
(eg) is a Rat
 To get a
Num
, you need to say 9.45e0
Rat IIII
for 0.0, 0.1 ... 1.0 > $x {
say $x;
}
loop ($x = 0.0; $x <= 1.0; $x += 0.1) {
say $x;
}
Rat V
 Suppose
my Rat $x = 4 / 5

$x.Str
is 0.8
(NYI on Niecza)

$x.perl
is 4/5
Rat VI
 In theory, this does not reduce unless needed or you call
.perl
 In practice, both Rakudo and Niecza reduce fraction
 The spec here feels like a halfformed idea which isn't consistent
 I plan to change the spec on this when no one is watching
 I mean, when I figure out the right thing to change it to
FatRat
 A rational type which is a full (infinite)
Int
over another (infinite) Int
 Only implemented in Niecza so far
 For slower but exact math
 Though note that methods like
.sin
are likely to only have Num
precision
Rational
 The spec defines a
Rational
parametric role which both Rat
and FatRat
do

Rat
is Rational[Int,uint64]
, FatRat
Rational[Int,Int]
 Makes great sense  most rational calculations don't care about precision internally
 Spec also mentions lowercase types, like
rat8
(which is Rational[int16,uint8]
)
 Nobody implements any of this yet
Complex
 Perl 6's basic complex number type is
Complex
 In Rakudo, it's two
Real
s
 In Niecza, it's two
Num
s
 The spec seems pretty vague on the matter
 The spec also mentions
complex
, which is presumably two num
s.
 Same methods that
Real
has, except a few that don't make sense
Trig functions
 Full suite of normal and hyperbolic functions
 Work on all builtin
Numeric
types
 In theory: Rakudo doesn't implement all the types and Niecza doesn't do Trig yet
 Actually properly supporting them on very large numbers is an interesting problem
 And I expect that
FatRat
support will lose precision by converting to Num
first
Tricky bits I

prefix:<>
(ie the negation operator) does not have as high a precedence as you might expect
 For instance,
1.abs
is 1
 That's because it parses as
(1.abs)
 Likewise,
1 ** 2
is also 1
 It's
(1 ** 2)
 Honest, that's how mathematicians do it!
Tricky bits II
 Math operations generally try to stay in the domain of the inputs
 For instance,
(1).sqrt
is NaN
 That's because it's real square root
 If you want a complex square root, you have to have a
Complex

(1).Complex.sqrt
is 0 + 1i
 Note that Niecza explicitly disavows this particular logic!
Tricky bits III
 The spec specifically forbids creating a
FatRat
unless requested
 This means that many operations which obviously could work won't
 For instance, in Niecza
1 / 10 ** 300 == 1E300
(ie a Num
)
 And
1 / 10 ** 1000 == 0
(closest a Num
can come)
Tricky bits IIII
 On the other hand,
FatRat.new(1, 1) / 10 ** 1000
is exact

1.FatRat
should probably also work, but it is NYI
 The flip side of this is that
FatRat
is sticky
 Even if the result could be represented as a normal
Rat
, it won't be
 ... actually, a method for checking and doing that conversion seems like it might be useful
Making new Real types
 Perl 6 has a generous set of builtin math types
 But people who deal with numbers always want more
 The
Real
role is designed to make it almost trivially easy
Money
class Money does Real {
has $.cents;
multi method new(Real $dollars) {
self.bless(*, :cents(($dollars * 100).Int));
}
multi method new(Int :$cents) {
self.bless(*, :$cents);
}
method Bridge() { $.cents.Bridge / 100.Bridge; }
method perl() { "Money.new(cents => $.cents)"; }
method isNaN() { $.cents.isNaN; }
}
Money II
 That's a fully functional, if somewhat useless
Real
type
 All
Real
operators and methods work on it
 Problem is it's not closed
 Any math you do with it will leave the class
 ie
Money + Money ~~ Num
Money III
multi sub infix:<+>(Money $a, Money $b) {
Money.new(cents => $a.cents + $b.cents);
}
Money IIII
 With that,
Money + Money ~~ Money
 Obviously, implementing the operators is still work
 But you only need to implement those that you must
 Everything else comes for free
Bridge
 The magic comes from the Bridge method
 The question is, how can two
Real
types which don't know about each other interact?
 The answer is to convert to a common core Perl 6 type they both know about
 The tricky part is, what should that type be?
Bridge II

Num
is a sensible compromise for accuracy and speed

FatRat
might be the choice of those requiring great precision
 Using
.Bridge
means your code doesn't have to make that choice
 Any
Real
method or sub which isn't overloaded for your type will call .Bridge
 That returns a type the Perl 6 core does know about
Bridge III
 What particular type is returned isn't specified in the spec
 If you implement your
.Bridge
method in terms of simpler .Bridge
methods, you don't need to know the actual type

method Bridge() { $.cents.Bridge / 100.Bridge; }
for instance
What about new nonreal numeric types?
 It's not clear to me how to have sensible default options for nonreal types
 I added a method
.reals
with the idea that you convert your item to a series of Real
s
 Then it can be compared with any other
Numeric
type, lexicographically Real
byReal
 Matches Larry's goal that any two
Numeric
types should be comparable
 Lots of people seem to hate this
Numbers & the bigger world of Perl 6
 Most numeric operators use
prefix:<+>
to convert nonNumeric
variables to Numeric
 This is the equivalent of calling the
.Numeric
method on that variable
 So
+"10" == 10.Numeric == 10
 Note that it is
Numeric
and not Real
 So it will not magically remove the imaginary part from
Complex
numbers
 This can sometimes cause unexpected results
Examples
# Find the 201st Fibonacci number
niecza> my @fib := 1, 1, *+* ... *; say @fib[200];
453973694165307953197296969697410619233826
Examples
# Find smallest number divisible by 2..20
sub dividesbyallupto($a, $b) {
!(2..$b).grep($a !%% *);
}
my $N = [*] 2, 3, 5, 7, 11, 13, 17, 19;
my @attempts := $N, 2 * $N
...
{ dividesbyallupto($_, 20) };
say @attempts[*1];
Examples
# Find smallest number divisible by 2..20
# sorear++ version
say [lcm] 2..20;
# answer is 232792560, btw
Examples
# inner core of a Mandelbrot set program
sub mandel(Complex $c) {
my $z = 0;
for ^$max_iterations {
$z = $z * $z + $c;
return True if $z.abs > 2;
}
return False;
}
Examples
POINT point = HwMakePoint (0.0, 0.0, 0.0);
double den = 0.0;
for (unsigned int i = 0; i <= degree; i++)
{
point += basis [i] * weights [span + i  degree] * points [span + i  degree];
den += basis [i] * weights [span + i  degree];
}
point = point / den;
Examples
# NURBS curve evaluation "loop" in p6
my $slice = span  degree .. span;
my @bw = basis[0 .. degree]
»*« weights[$slice];
my $point = ([+] @bw »*« points[$slice])
/ [+] @bw;
Talk Online
 Should be the top post on http://justrakudoit.wordpress.com for a few days
 Or feel free to ping me on the
#perl6
IRC channel on freenode
 Or email me at colomon@gmail.com
 Or grab me in the hallway
 Thanks to Moritz Lenz for the presentation software
 Thanks to moritz, TimToady, and PerlJam for suggestions
 Thanks to you for listening!