Complex numbers#

Instead of a+bi or a+b×i, APL uses aJb for scalar atomic complex numbers. In other words, 3+4i is 3J4 and i is 0J1. The arithmetic functions support complex mathematics where sensible. Of special interest are monadic + and | and the circular functions k○Y. Monadic + is the complex conjugate, that is, a+bi a-bi.

2J4*0.5
1.79890744J1.111785941

We can combine a real and imaginary parts with re+0J1×im but since the complex numbers are atomic (simple scalars) we need a way to split them. For this we have 9○Y and 11○Y which would be Re(Y) and Im(Y) in traditional notation. You might think it odd that we have numbered functions (like the trigonometric functions; sine and cosine are 1○Y and 2○Y) but it can actually be really neat because is a scalar function.

Let’s say we have a vector of complex numbers Nv←2J3 0J1 10 then how might we get a 2-row matrix with one row for the real parts and one row for the complex part?

Nv2J3 0J1 10
9 11∘.Nv 
2 0 10
3 1  0

Now, if we have an array N←2 2⍴2J3 0J1 10 0 and want a two-element vector where each element has the same shape as N but the first has the real parts and the second the imaginary parts?

N2 22J3 0J1 10 0
9 11○⊂N
┌────┬───┐
│ 2 0│3 1│
│10 0│0 0│
└────┴───┘

The solution can be either a tacit function (9 11○⊂)N or the expression 9 11○⊂N though the outcome is equivalent. First we enclose N which makes it a scalar. Then we pair that scalar with a vector 9 11 as arguments to a scalar function, . This makes APL do a scalar extension: 9 11○(⍴9 11)⍴⊂N or 9 11○N N or (9○N)(11○N).

Now, if you’re familiar with the trigonometric functions, you’ll know that negating the left argument of gives you the inverse function. For example, sin is 1○Y and arcsin is ¯1○Y. So 11○Y extracts the imaginary part into a real number. ¯11○Y will “put back” a real number into its imaginary place:

¯113
0J3

Of course, it can’t restore the real part, as that was discarded. So… given our 2-element real-and-complex vector from above, how can we reconstitute our original N? In other words, how can we convert (2 0)(3 1) back to 2J3 0J1?

⊃+/¯9 ¯11(2 0)(3 1)
2J3 0J1

If the argument is a matrix, we can use

N2 22J3 0J1 10 0
m9 11○⊂N
¯9 ¯11+.9 11○⊂m
┌────┬───┐
│ 2 0│3 1│
│10 0│0 0│
└────┴───┘

If you deal with complex numbers a lot, you might want to define J←{⍺+0j1×⍵} which will then allow you to write a J b to form aJb, and so ⊃J/vec for this challenge.

Complex numbers are not just for hard-core mathematicians. Sometimes they are convenient to use as simple scalar 2D coordinates, where the real part represents offset along one axis, and the imaginary part along the other. One benefit in doing so is that some formulas become vastly simpler with this representation. Let’s say we have two points in 2D space, (a,b) and (x,y), and we want to compute the distance between them. The traditional approach is something like this:

(a b x y)4 6 1 2
0.5*+/2*a b-x y
5

Let’s rewrite it given (u v)←4j6 1j2:

(u v)4j6 1j2
|u-v 
5

This lends itself nicely to a 2-train:

Dist|-
u Dist v
5

Now imagine you need to represent some vectors in 2D space. 3j3 would point north-east. We can now rotate the pointer 90 degrees counter-clockwise, with 0j1×3j3:

0J1×3J3
¯3J3

Now it points north-west instead. Using 0J¯1× will rotate clockwise instead. Also, multiplication by ¯1 (which is 0J1*2) and so rotation by 180 degrees, giving us the oppositely pointed vector, and further multiplication by 0J1 (i.e. to 0J1*3) is 270 degrees. This means we can get the four corners with 3j3×0j1*⍳4. Similarly, we can get the four cardinal directions with 3J0×0J1*⍳4:

3J3×0J1*⍳4
3×0J1*⍳4
¯3J3 ¯3J¯3 3J¯3 3J3
0J3 ¯3 0J¯3 3

Some more cheatsheet about vectors: +v is reflection by x-axis, +-v is by y-axis, |v is length, ×v is unit vector in that direction, k××v is vector of length k in that direction. If you want to scale vector v with scaling factor k, do k×v, and to rotate vector v by the angle of vector w, do v××w.

You can represent a number of “moves” in 2D space as complex vectors, say moves←1j2 0j3 ¯1j0. This means move 1 right and 2 up, then 3 right, then 1 down. Given such a moves sequence, where do we end up?

moves1j2 0j3 ¯1j0
+/moves
0J5

What points did we pass through?

+\moves
1J2 1J5 0J5

Although we may want to say

+\0,moves
0 1J2 1J5 0J5

to include the origin.

Conversely, given a set of points, what is the corresponding moves sequence?

2-⍨/0 1J2 1J5 0J5
1J2 0J3 ¯1

Sometimes, it is convenient to deal with the angle (upwards from due east) and magnitude (pointer length) instead of the “coordinates”. We can already get the magnitude (absolute value) with |Y but the angle (or phase) is 12○Y. Side note: the 12○Y is the one called atan2() in other languages. Remember how convenient it was to use the scalar function with a 2-element left argument 9 11. For that same reason, |Y exists as argument, which is 10 of course. So 10 12○Y gives you the magnitude and phase. Of course, we can use 10 12∘.○Y and 10 12○⊂Y like before.

How about the other way, if we have an angle and magnitude and want to combine them into a single complex number? Remember how we used {⍺+¯11○⍵} before. This is then {⍺ׯ12○⍵} (or, if you prefer, ¯10 ¯12×.○Y).