---
title: "Dyalog APL Problem Solving Competition 2020"
subtitle: "Annotated Solutions"
date: 2020-07-31
toc: true
---

* Phase I

#+begin_src default
  :Namespace Phase1
#+end_src

** 1. Let's Split!

#+begin_quote
Write a function that, given a right argument ~Y~ which is a scalar or
a non-empty vector and a left argument ~X~ which is a single non-zero
integer so that its absolute value is less or equal to ~≢Y~, splits
~Y~ into a vector of two vectors according to ~X~, as follows:
- If ~X>0~, the first vector contains the first ~X~ elements of ~Y~
  and the second vector contains the remaining elements.
- If ~X<0~, the second vector contains the last ~|X~ elements of ~Y~
  and the first vector contains the remaining elements.
#+end_quote

*Solution:* ~(0>⊣)⌽((⊂↑),(⊂↓))~

There are three nested trains here. The first one, ~((⊂↑),(⊂↓))~, uses
the two functions [[https://help.dyalog.com/18.0/index.htm#Language/Primitive%20Functions/Take.htm][Take]] (~↑~) and [[https://help.dyalog.com/18.0/index.htm#Language/Primitive%20Functions/Drop.htm][Drop]] (~↓~) to build a nested array
consisting of the two outputs we need. (Take and Drop already have the
behaviour needed regarding negative arguments.) However, if the left
argument is positive, the two arrays will not be in the correct
order. So we need a way to reverse them if ~X<0~.

The second train ~(0>⊣)~ will return 1 if its left argument is
positive. From this, we can use [[https://help.dyalog.com/18.0/index.htm#Language/Primitive%20Functions/Rotate.htm][Rotate]] (~⌽~) to correctly order the
nested array, in the last train.

** 2. Character Building

#+begin_quote
UTF-8 encodes Unicode characters using 1-4 integers for each
character. Dyalog APL includes a system function, ~⎕UCS~, that can
convert characters into integers and integers into characters. The
expression ~'UTF-8'∘⎕UCS~ converts between characters and UTF-8.

Consider the following:

      #+begin_src default
      'UTF-8'∘⎕UCS 'D¥⍺⌊○9'
68 194 165 226 141 186 226 140 138 226 151 139 57
      'UTF-8'∘⎕UCS 68 194 165 226 141 186 226 140 138 226 151 139 57
D¥⍺⌊○9
      #+end_src

How many integers does each character use?

      #+begin_src default
      'UTF-8'∘⎕UCS¨ 'D¥⍺⌊○9' ⍝ using ]Boxing on
┌──┬───────┬───────────┬───────────┬───────────┬──┐
│68│194 165│226 141 186│226 140 138│226 151 139│57│
└──┴───────┴───────────┴───────────┴───────────┴──┘      
      #+end_src

The rule is that an integer in the range 128 to 191 (inclusive)
continues the character of the previous integer (which may itself be a
continuation). With that in mind, write a function that, given a right
argument which is a simple integer vector representing valid UTF-8
text, encloses each sequence of integers that represent a single
character, like the result of ~'UTF-8'∘⎕UCS¨'UTF-8'∘⎕UCS~ but does not
use any system functions (names beginning with ~⎕~)
#+end_quote

*Solution:* ~{(~⍵∊127+⍳64)⊂⍵}~

First, we build a binary array from the string, encoding each
continuation character as 0, and all the others as 1. Next, we can use
this binary array with [[https://help.dyalog.com/latest/#Language/Primitive%20Functions/Partitioned%20Enclose.htm][Partitioned Enclose]] (~⊂~) to return the correct
output.

** 3. Excel-lent Columns

#+begin_quote
A Microsoft Excel spreadsheet numbers its rows counting up
from 1. However Excel's columns are labelled alphabetically —
beginning with A–Z, then AA–AZ, BA–BZ, up to ZA–ZZ, then AAA–AAZ and
so on.

Write a function that, given a right argument which is a character
scalar or non-empty vector representing a valid character Excel column
identifier between A and XFD, returns the corresponding column number
#+end_quote

*Solution:* ~26⊥⎕A∘⍳~

We use the alphabet ~⎕A~ and [[https://help.dyalog.com/latest/#Language/Primitive%20Functions/Index%20Of.htm][Index Of]] (~⍳~) to compute the index in
the alphabet of every character. As a train, this can be done by
~(⎕A∘⍳)~. We then obtain an array of numbers, each representing a
letter from 1 to 26. The [[https://help.dyalog.com/latest/#Language/Primitive%20Functions/Decode.htm][Decode]] (~⊥~) function can then turn this
base-26 number into the expected result.

** 4. Take a Leap

#+begin_quote
Write a function that, given a right argument which is an integer
array of year numbers greater than or equal to 1752 and less than
4000, returns a result of the same shape as the right argument where 1
indicates that the corresponding year is a leap year (0 otherwise).

A leap year algorithm can be found [[https://en.wikipedia.org/wiki/Leap_year#Algorithm][here]].
#+end_quote

*Solution:* ~1 3∊⍨(0+.=400 100 4∘.|⊢)~

According to the algorithm, a year is a leap year in two situations:
- if it is divisible by 4, but not 100 (and therefore not 400),
- if it is divisible by 400 (and therefore 4 and 100 as well).

The train ~(400 100 4∘.|⊢)~ will test if each year in the right
argument is divisible by 400, 100, and 4, using an [[https://help.dyalog.com/latest/#Language/Primitive%20Operators/Outer%20Product.htm][Outer Product]]. We
then use an [[https://help.dyalog.com/latest/#Language/Primitive%20Operators/Inner%20Product.htm][Inner Product]] to count how many times each year is
divisible by one of these numbers. If the count is 1 or 3, it is a
leap year. Note that we use [[https://help.dyalog.com/latest/#Language/Primitive%20Operators/Commute.htm][Commute]] (~⍨~) to keep the dfn as a train,
and to preserve the natural right-to-left reading of the algorithm.

** 5. Stepping in the Proper Direction

#+begin_quote
Write a function that, given a right argument of 2 integers, returns a
vector of the integers from the first element of the right argument to
the second, inclusively.
#+end_quote

*Solution:* ~{(⊃⍵)+(-×-/⍵)×0,⍳|-/⍵}~

First, we have to compute the range of the output, which is the
absolute value of the difference between the two integers ~|-/⍵~. From
this, we compute the actual sequence, including zero: ~0,⍳|-/⍵~.

This sequence will always be nondecreasing, but we have to make it
decreasing if needed, so we multiply it by the opposite of the sign of
~-/⍵~. Finally, we just have to start the sequence at the first
element of ~⍵~.

** 6. Please Move to the Front

#+begin_quote
Write a function that, given a right argument which is an integer
vector and a left argument which is an integer scalar, reorders the
right argument so any elements equal to the left argument come first
while all other elements keep their order.
#+end_quote

*Solution:* ~{⍵[⍋⍺≠⍵]}~

~⍺≠⍵~ will return a binary vector marking as 0 all elements equal to
the left argument. Using this index to sort in the usual way with
[[https://help.dyalog.com/latest/#Language/Primitive%20Functions/Grade%20Up%20Monadic.htm][Grade Up]] will return the expected result.

** 7. See You in a Bit

#+begin_quote
A common technique for encoding a set of on/off states is to use a
value of $2^n$ for the state in position $n$ (origin 0), 1 if the
state is "on" or 0 for "off" and then add the values. Dyalog APL's
[[https://help.dyalog.com/17.1/#Language/APL%20Component%20Files/Component%20Files.htm#File_Access_Control][component file permission codes]] are an example of this. For example,
if you wanted to grant permissions for read (access code 1), append
(access code 8) and rename (access code 128) then the resulting code
would be 137 because that's 1 + 8 + 128.

Write a function that, given a non-negative right argument which is an
integer scalar representing the encoded state and a left argument
which is an integer scalar representing the encoded state settings
that you want to query, returns 1 if all of the codes in the left
argument are found in the right argument (0 otherwise).
#+end_quote

*Solution:* ~{f←⍸∘⌽(2∘⊥⍣¯1)⋄∧/(f⍺)∊f⍵}~

The difficult part is to find the set of states for an integer. We
need a function that will return ~1 8 128~ (or an equivalent
representation) for an input of ~137~. To do this, we need the base-2
representations of $137 = 1 + 8 + 128 = 2^0 + 2^3 + 2^7 =
10010001_2$. The function ~(2∘⊥⍣¯1)~ will return the base-2
representation of its argument, and by [[https://help.dyalog.com/latest/#Language/Primitive%20Functions/Reverse.htm][reversing]] and finding [[https://help.dyalog.com/latest/#Language/Primitive%20Functions/Where.htm][where]] the
non-zero elements are, we find the correct exponents (~1 3 7~ in this
case). That is what the function ~f~ does.

Next, we just need to check that all elements of ~f⍺~ are also in
~f⍵~.

** 8. Zigzag Numbers

#+begin_quote
A zigzag number is an integer in which the difference in magnitude of
each pair of consecutive digits alternates from positive to negative
or negative to positive.

Write a function that takes a single integer greater than or equal to
100 and less than 10^{15} as its right argument and returns a 1 if the
integer is a zigzag number, 0 otherwise.
#+end_quote

*Solution:* ~∧/2=∘|2-/∘×2-/(10∘⊥⍣¯1)~

TODO

** 9. Rise and Fall

#+begin_quote
Write a function that, given a right argument which is an integer
scalar or vector, returns a 1 if the values of the right argument
conform to the following pattern (0 otherwise):

- The elements increase or stay the same until the "apex" (highest
  value) is reached
- After the apex, any remaining values decrease or remain the same
#+end_quote

*Solution:* ~{∧/(⍳∘≢≡⍋)¨(⊂((⊢⍳⌈/)↑⊢),⍵),⊂⌽((⊢⍳⌈/)↓⊢),⍵}~

TODO

** 10. Stacking It Up

#+begin_quote
Write a function that takes as its right argument a vector of simple
arrays of rank 2 or less (scalar, vector, or matrix). Each simple
array will consist of either non-negative integers or printable ASCII
characters. The function must return a simple character array that
displays identically to what ~{⎕←⍵}¨~ displays when applied to the
right argument.
#+end_quote

*Solution:* ~{↑⊃,/↓¨⍕¨⍵}~

TODO

#+begin_src default
  :EndNamespace
#+end_src

* Phase II

#+begin_src default
  :Namespace Contest2020

	  :Namespace Problems
		  (⎕IO ⎕ML ⎕WX)←1 1 3
#+end_src

** Problem 1 -- Take a Dive

#+begin_src default
  ∇ score←dd DiveScore scores
    :If 7=≢scores
	    scores←scores[¯2↓2↓⍋scores]
    :ElseIf 5=≢scores
	    scores←scores[¯1↓1↓⍋scores]
    :Else
	    scores←scores
    :EndIf
    score←2(⍎⍕)dd×+/scores
  ∇
#+end_src

** Problem 2 -- Another Step in the Proper Direction

#+begin_src default
  ∇ steps←{p}Steps fromTo;segments;width
    width←|-/fromTo
    :If 0=⎕NC'p' ⍝ No left argument: same as Problem 5 of Phase I
	    segments←0,⍳width
    :ElseIf p<0 ⍝ -⌊p is the number of equally-sized steps to take
	    segments←(-⌊p){0,⍵×⍺÷⍨⍳⍺}width
    :ElseIf p>0 ⍝ p is the step size
	    segments←p{⍵⌊⍺×0,⍳⌈⍵÷⍺}width
    :ElseIf p=0 ⍝ As if we took zero step
	    segments←0
    :EndIf
    ⍝ Take into account the start point and the direction.
    steps←fromTo{(⊃⍺)+(-×-/⍺)×⍵}segments
  ∇
#+end_src


** Problem 3 -- Past Tasks Blast

#+begin_src default
  ∇ urls←PastTasks url;r;paths
    r←HttpCommand.Get url
    paths←('[a-zA-Z0-9_/]+\.pdf'⎕S'&')r.Data
    urls←('https://www.dyalog.com/'∘,)¨paths
  ∇
#+end_src

** Problem 4 -- Bioinformatics

#+begin_src default
  ⍝ Test if a DNA string is a reverse palindrome.
  isrevp←{⍵≡⌽'TAGC'['ATCG'⍳⍵]}

  ⍝ Generate all subarrays (position, length) pairs, for
  ⍝ 4 ≤ length ≤ 12.
  subarrays←{⊃,/(⍳⍵),¨¨3↓¨⍳¨12⌊1+⍵-⍳⍵}

  ∇ r←revp dna;positions
    positions←subarrays⍴dna
    ⍝ Filter subarrays which are reverse palindromes.
    r←↑({isrevp dna[¯1+⍵[1]+⍳⍵[2]]}¨positions)/positions
  ∇
#+end_src

#+begin_src default
  sset←{((1E6|2∘×)⍣⍵)1}
#+end_src

** Problem 5 -- Future and Present Value

#+begin_src default
  ⍝ First solution: ((1+⊢)⊥⊣) computes the total return
  ⍝ for a vector of amounts ⍺ and a vector of rates
  ⍝ ⍵. It is applied to every prefix subarray of amounts
  ⍝ and rates to get all intermediate values. However,
  ⍝ this has quadratic complexity.
  ⍝ rr←(,\⊣)((1+⊢)⊥⊣)¨(,\⊢)

  ⍝ Second solution: We want to be able to use the
  ⍝ recurrence relation (recur) and scan through the
  ⍝ vectors of amounts and rates, accumulating the total
  ⍝ value at every time step. However, APL evaluation is
  ⍝ right-associative, so a simple Scan
  ⍝ (recur\amounts,¨values) would not give the correct
  ⍝ result, since recur is not associative and we need
  ⍝ to evaluate it left-to-right. (In any case, in this
  ⍝ case, Scan would have quadratic complexity, so would
  ⍝ not bring any benefit over the previous solution.)
  ⍝ What we need is something akin to Haskell's scanl
  ⍝ function, which would evaluate left to right in O(n)
  ⍝ time. This is what we do here, accumulating values
  ⍝ from left to right. (This is inspired from
  ⍝ dfns.ascan, although heavily simplified.)
  rr←{recur←{⍵[1]+⍺×1+⍵[2]} ⋄ 1↓⌽⊃{(⊂(⊃⍵)recur⍺),⍵}/⌽⍺,¨⍵}
#+end_src

#+begin_src default
  ⍝ Simply apply the formula for cashflow calculations.
  pv←{+/⍺÷×\1+⍵}
#+end_src

** Problem 6 -- Merge

#+begin_src default
  ∇ val←ns getval var
    :If ''≡var ⍝ literal '@'
	    val←'@'
    :ElseIf (⊂var)∊ns.⎕NL ¯2
	    val←⍕ns⍎var
    :Else
	    val←'???'
    :EndIf
  ∇
#+end_src

#+begin_src default
  ∇ text←templateFile Merge jsonFile;template;ns
    template←⊃⎕NGET templateFile 1
    ns←⎕JSON⊃⎕NGET jsonFile
    ⍝ We use a simple regex search and replace on the
    ⍝ template.
    text←↑('@[a-zA-Z]*@'⎕R{ns getval ¯1↓1↓⍵.Match})template
  ∇
#+end_src

** Problem 7 -- UPC

#+begin_src default
  CheckDigit←{10|-⍵+.×11⍴3 1}
#+end_src

#+begin_src default
  ⍝ Left and right representations of digits. Decoding
  ⍝ the binary representation from decimal is more
  ⍝ compact than writing everything explicitly.
  lrepr←⍉(7⍴2)⊤13 25 19 61 35 49 47 59 55 11
  rrepr←~¨lrepr
#+end_src

#+begin_src default
  ∇ bits←WriteUPC digits;left;right
    :If (11=≢digits)∧∧/digits∊0,⍳9
	    left←,lrepr[1+6↑digits;]
	    right←,rrepr[1+6↓digits,CheckDigit digits;]
	    bits←1 0 1,left,0 1 0 1 0,right,1 0 1
    :Else
	    bits←¯1
    :EndIf
  ∇
#+end_src

#+begin_src default
  ∇ digits←ReadUPC bits
    :If 95≠⍴bits ⍝ incorrect number of bits
	    digits←¯1
    :Else
	    ⍝ Test if the barcode was scanned right-to-left.
	    :If 0=2|+/bits[3+⍳7]
		    bits←⌽bits
	    :EndIf
	    digits←({¯1+lrepr⍳⍵}¨(7/⍳6)⊆42↑3↓bits),{¯1+rrepr⍳⍵}¨(7/⍳6)⊆¯42↑¯3↓bits
	    :If ~∧/digits∊0,⍳9 ⍝ incorrect parity
		    digits←¯1
	    :ElseIf (⊃⌽digits)≠CheckDigit ¯1↓digits ⍝ incorrect check digit
		    digits←¯1
	    :EndIf
    :EndIf
  ∇
#+end_src

** Problem 8 -- Balancing the Scales

#+begin_src default
  ∇ parts←Balance nums;subsets;partitions
    ⍝ This is a brute force solution, running in
    ⍝ exponential time. We generate all the possible
    ⍝ partitions, filter out those which are not
    ⍝ balanced, and return the first matching one. There
    ⍝ are more advanced approach running in
    ⍝ pseudo-polynomial time (based on dynamic
    ⍝ programming, see the "Partition problem" Wikipedia
    ⍝ page), but they are not warranted here, as the
    ⍝ input size remains fairly small.

    ⍝ Generate all partitions of a vector of a given
    ⍝ size, as binary mask vectors.
    subsets←{1↓2⊥⍣¯1⍳2*⍵}
    ⍝ Keep only the subsets whose sum is exactly
    ⍝ (+/nums)÷2.
    partitions←nums{((2÷⍨+/⍺)=⍺+.×⍵)/⍵}subsets⍴nums
    :If 0=≢,partitions
	    ⍝ If no partition satisfy the above
	    ⍝ criterion, we return ⍬.
	    parts←⍬
    :Else
	    ⍝ Otherwise, we return the first possible
	    ⍝ partition.
	    parts←nums{((⊂,(⊂~))⊃↓⍉⍵)/¨2⍴⊂⍺}partitions
    :EndIf
  ∇
#+end_src

** Problem 9 -- Upwardly Mobile

#+begin_src default
  ∇ weights←Weights filename;mobile;branches;mat
    ⍝ Put your code and comments below here

    ⍝ Parse the mobile input file.
    mobile←↑⊃⎕NGET filename 1
    branches←⍸mobile∊'┌┴┐'
    ⍝ TODO: Build the matrix of coefficients mat.

    ⍝ Solve the system of equations (arbitrarily setting
    ⍝ the first variable at 1 because the system is
    ⍝ overdetermined), then multiply the coefficients by
    ⍝ their least common multiple to get the smallest
    ⍝ integer weights.
    weights←((1∘,)×(∧/÷))mat[;1]⌹1↓[2]mat
  ∇
#+end_src

#+begin_src default
	  :EndNamespace
  :EndNamespace
#+end_src

* General Remarks
