/* ARM assembly Raspberry PI  */
/*  program patienceSort.s  */

 /* REMARK 1 : this program use routines in a include file
   see task Include a file language arm assembly
   for the routine affichageMess conversion10
   see at end of this program the instruction include */
/* for constantes see task include a file in arm assembly */
/************************************/
/* Constantes                       */
/************************************/
.include "../constantes.inc"

.include "../../ficmacros.s"
/*******************************************/
/* Structures                               */
/********************************************/
/* structure Doublylinkedlist*/
    .struct  0
dllist_head:                    @ head node
    .struct  dllist_head + 4
dllist_tail:                    @ tail node
    .struct  dllist_tail  + 4
dllist_fin:
/* structure Node Doublylinked List*/
    .struct  0
NDlist_next:                    @ next element
    .struct  NDlist_next + 4
NDlist_prev:                    @ previous element
    .struct  NDlist_prev + 4
NDlist_value:                   @ element value or key
    .struct  NDlist_value + 4
NDlist_fin:

/*********************************/
/* Initialized data              */
/*********************************/
.data
szMessSortOk:       .asciz "Table sorted.\n"
szMessSortNok:      .asciz "Table not sorted !!!!!.\n"
sMessResult:        .asciz "Value  : @ \n"
szCarriageReturn:   .asciz "\n"

.align 4
TableNumber:      .int   1,11,3,6,2,5,9,10,8,4,7
#TableNumber:       .int   10,9,8,7,6,5,4,3,2,1
                   .equ NBELEMENTS, (. - TableNumber) / 4
/*********************************/
/* UnInitialized data            */
/*********************************/
.bss
sZoneConv:            .skip 24
/*********************************/
/*  code section                 */
/*********************************/
.text
.global main
main:                                              @ entry of program

    ldr r0,iAdrTableNumber                         @ address number table
    mov r1,#0                                      @ first element
    mov r2,#NBELEMENTS                             @ number of élements
    bl patienceSort
    ldr r0,iAdrTableNumber                         @ address number table
    bl displayTable

    ldr r0,iAdrTableNumber                         @ address number table
    mov r1,#NBELEMENTS                             @ number of élements
    bl isSorted                                    @ control sort
    cmp r0,#1                                      @ sorted ?
    beq 1f
    ldr r0,iAdrszMessSortNok                       @ no !! error sort
    bl affichageMess
    b 100f
1:                                                 @ yes
    ldr r0,iAdrszMessSortOk
    bl affichageMess
100:                                               @ standard end of the program
    mov r0, #0                                     @ return code
    mov r7, #EXIT                                  @ request to exit program
    svc #0                                         @ perform the system call

iAdrszCarriageReturn:     .int szCarriageReturn
iAdrsMessResult:          .int sMessResult
iAdrTableNumber:          .int TableNumber
iAdrszMessSortOk:         .int szMessSortOk
iAdrszMessSortNok:        .int szMessSortNok
/******************************************************************/
/*     control sorted table                                   */
/******************************************************************/
/* r0 contains the address of table */
/* r1 contains the number of elements  > 0  */
/* r0 return 0  if not sorted   1  if sorted */
isSorted:
    push {r2-r4,lr}                                    @ save registers
    mov r2,#0
    ldr r4,[r0,r2,lsl #2]
1:
    add r2,#1
    cmp r2,r1
    movge r0,#1
    bge 100f
    ldr r3,[r0,r2, lsl #2]
    cmp r3,r4
    movlt r0,#0
    blt 100f
    mov r4,r3
    b 1b
100:
    pop {r2-r4,lr}
    bx lr                                              @ return

/******************************************************************/
/*         patience sort                                                   */
/******************************************************************/
/* r0 contains the address of table */
/* r1 contains first start index
/* r2 contains the number of elements  */
patienceSort:
    push {r1-r9,lr}            @ save registers
    lsl r9,r2,#1               @ compute total size of piles (2 list pointer by pile )
    lsl r10,r9,#2              @ 4 bytes by number
    sub sp,sp,r10              @ reserve place to stack
    mov fp,sp                  @ frame pointer = stack
    mov r3,#0                  @ index
    mov r4,#0
1:
    str r4,[fp,r3,lsl #2]      @ init piles area
    add r3,r3,#1               @ increment index
    cmp r3,r9
    blt 1b
    mov r3,#0                  @ index value
    mov r4,#0                  @ counter first pile
    mov r8,r0                  @ save table address
2:
    ldr r1,[r8,r3,lsl #2]      @ load value
    add r0,fp,r4,lsl #3        @ pile address
    bl isEmpty
    cmp r0,#0                  @ pile empty ?
    bne 3f
    add r0,fp,r4,lsl #3        @ pile address
    bl insertHead              @ insert value r1
    b 5f
3:
    add r0,fp,r4,lsl #3        @ pile address
    ldr r5,[r0,#dllist_head]
    ldr r5,[r5,#NDlist_value]  @ load first list value
    cmp r1,r5                  @ compare value and last value on the pile
    blt 4f
    add r0,fp,r4,lsl #3        @ pile address
    bl insertHead              @ insert value r1
    b 5f
4:                             @ value is smaller créate a new pile
    add r4,r4,#1
    add r0,fp,r4,lsl #3        @ pile address
    bl insertHead              @ insert value r1
5:
    add r3,r3,#1               @ increment index value
    cmp r3,r2                  @ end
    blt 2b                     @ and loop

    /* step 2 */
    mov r6,#0                 @ index value table
6:
    mov r3,#0                 @ index pile
    mov r5,# 1<<30            @ min
7:                            @ search minimum
    add r0,fp,r3,lsl #3
    bl isEmpty
    cmp r0,#0
    beq 8f
    add r0,fp,r3,lsl #3
    bl searchMinList
    cmp r0,r5                 @ compare min global
    movlt r5,r0               @ smaller -> store new min
    movlt r7,r1               @ and pointer to min
    addlt r9,fp,r3,lsl #3     @ and head list
8:
    add r3,r3,#1              @ next pile
    cmp r3,r4                 @ end ?
    ble 7b
    str r5,[r8,r6,lsl #2]     @ store min to table value
    mov r0,r9                 @ and suppress the value in the pile
    mov r1,r7
    bl suppressNode
    add r6,r6,#1              @ increment index value
    cmp r6,r2                 @ end ?
    blt 6b

    add sp,sp,r10             @ stack alignement
100:
    pop {r1-r9,lr}
    bx lr                     @ return

/******************************************************************/
/*      Display table elements                                */
/******************************************************************/
/* r0 contains the address of table */
displayTable:
    push {r0-r3,lr}                                    @ save registers
    mov r2,r0                                          @ table address
    mov r3,#0
1:                                                     @ loop display table
    ldr r0,[r2,r3,lsl #2]
    ldr r1,iAdrsZoneConv                               @
    bl conversion10S                                    @ décimal conversion
    ldr r0,iAdrsMessResult
    ldr r1,iAdrsZoneConv                               @ insert conversion
    bl strInsertAtCharInc
    bl affichageMess                                   @ display message
    add r3,#1
    cmp r3,#NBELEMENTS - 1
    ble 1b
    ldr r0,iAdrszCarriageReturn
    bl affichageMess
    mov r0,r2
100:
    pop {r0-r3,lr}
    bx lr
iAdrsZoneConv:           .int sZoneConv
/******************************************************************/
/*     list is empty ?                         */
/******************************************************************/
/* r0 contains the address of the list structure */
/* r0 return 0 if empty  else return 1 */
isEmpty:
    ldr r0,[r0,#dllist_head]
    cmp r0,#0
    movne r0,#1
    bx lr                                @ return
/******************************************************************/
/*     insert value at list head                        */
/******************************************************************/
/* r0 contains the address of the list structure */
/* r1 contains value */
insertHead:
    push {r1-r4,lr}                         @ save  registers
    mov r4,r0                            @ save address
    mov r0,r1                            @ value
    bl createNode
    cmp r0,#-1                           @ allocation error ?
    beq 100f
    ldr r2,[r4,#dllist_head]             @ load address first node
    str r2,[r0,#NDlist_next]             @ store in next pointer on new node
    mov r1,#0
    str r1,[r0,#NDlist_prev]             @ store zero in previous pointer on new node
    str r0,[r4,#dllist_head]             @ store address new node in address head list
    cmp r2,#0                            @ address first node is null ?
    strne r0,[r2,#NDlist_prev]           @ no store adresse new node in previous pointer
    streq r0,[r4,#dllist_tail]           @ else store new node in tail address
100:
    pop {r1-r4,lr}                       @ restaur registers
    bx lr                                @ return

/******************************************************************/
/*     search value minimum                                               */
/******************************************************************/
/* r0 contains the address of the list structure */
/* r0 return min   */
/* r1 return address of node */
searchMinList:
    push {r2,r3,lr}                         @ save  registers
    ldr r0,[r0,#dllist_head]             @ load first node
    mov r3,#1<<30
    mov r1,#0
1:
    cmp r0,#0                            @ null -> end
    moveq r0,r3
    beq 100f
    ldr r2,[r0,#NDlist_value]            @ load node value
    cmp r2,r3                            @ min ?
    movlt r3,r2                            @ value -> min
    movlt r1,r0                            @ store pointer
    ldr r0,[r0,#NDlist_next]             @ load addresse next node
    b 1b                                 @ and loop
100:
    pop {r2,r3,lr}                          @ restaur registers
    bx lr                                @ return
/******************************************************************/
/*     suppress node                                               */
/******************************************************************/
/* r0 contains the address of the list structure */
/* r1 contains the address to node to suppress  */
suppressNode:
    push {r2,r3,lr}                      @ save  registers
    ldr r2,[r1,#NDlist_next]             @ load addresse next node
    ldr r3,[r1,#NDlist_prev]             @ load addresse prev node
    cmp r3,#0
    strne r2,[r3,#NDlist_next]
    streq r3,[r0,#NDlist_next]
    cmp r2,#0
    strne r3,[r2,#NDlist_prev]
    streq r2,[r0,#NDlist_prev]
100:
    pop {r2,r3,lr}                       @ restaur registers
    bx lr                                @ return
/******************************************************************/
/*     Create new node                                            */
/******************************************************************/
/* r0 contains the value */
/* r0 return node address or -1 if allocation error*/
createNode:
    push {r1-r7,lr}                         @ save  registers
    mov r4,r0                            @ save value
    @ allocation place on the heap
    mov r0,#0                                   @ allocation place heap
    mov r7,#0x2D                                @ call system 'brk'
    svc #0
    mov r5,r0                                   @ save address heap for output string
    add r0,#NDlist_fin                            @ reservation place one element
    mov r7,#0x2D                                @ call system 'brk'
    svc #0
    cmp r0,#-1                                  @ allocation error
    beq 100f
    mov r0,r5
    str r4,[r0,#NDlist_value]                   @ store value
    mov r2,#0
    str r2,[r0,#NDlist_next]                    @ store zero to pointer next
    str r2,[r0,#NDlist_prev]                    @ store zero to pointer previous
100:
    pop {r1-r7,lr}                          @ restaur registers
    bx lr                                   @ return
/***************************************************/
/*      ROUTINES INCLUDE                           */
/***************************************************/
.include "../affichage.inc"


(*------------------------------------------------------------------*)

#include "share/atspre_staload.hats"

vtypedef array_tup_vt (a : [email protected]+, p : addr, n : int) =
  (* An array, without size information attached. *)
  @(array_v (a, p, n),
    mfree_gc_v p |
    ptr p)

extern fn {a : [email protected]}
patience_sort
          {ifirst, len   : int | 0 <= ifirst}
          {n             : int | ifirst + len <= n}
          (arr           : &RD(array (a, n)),
           ifirst        : size_t ifirst,
           len           : size_t len)
    :<!wrt> (* Return an array of indices into arr. *)
            [p : addr]
            array_tup_vt
              ([i : int | len == 0 ||
                          (ifirst <= i && i < ifirst + len)] size_t i,
               p, len)

(* patience_sort$lt : the order predicate. *)
extern fn {a : [email protected]}
patience_sort$lt (x : a, y : a) :<> bool

(*------------------------------------------------------------------*)
(*

  In the following implementation of next_power_of_two:

    * I implement it as a template for all types of kind g1uint. This
      includes dependent forms of uint, usint, ulint, ullint, size_t,
      and yet more types in the prelude; also whatever others one may
      create.

    * I prove the result is not less than the input.

    * I prove the result is less than twice the input.

    * I prove the result is a power of two. This last proof is
      provided in the form of an EXP2 prop.

    * I do NOT return what number two is raised to (though I easily
      could have). I leave that number "existentially defined". In
      other words, I prove only that some such non-negative number
      exists.

*)

fn {tk : tkind}
next_power_of_two
          {i : pos}
          (i : g1uint (tk, i))
    :<> [k : int | i <= k; k < 2 * i]
        [n : nat]
        @(EXP2 (n, k) | g1uint (tk, k)) =
  let
    (* This need not be a fast implementation. *)

    val one : g1uint (tk, 1) = g1u2u 1u

    fun
    loop {j  : pos | j < i} ..
         (pf : [n : nat] EXP2 (n, j) |
          j  : g1uint (tk, j))
        :<> [k : int | i <= k; k < 2 * i]
            [n : nat]
            @(EXP2 (n, k) | g1uint (tk, k)) =
      let
        val j2 = j + j
      in
        if i <= j2 then
          @(EXP2ind pf | j2)
        else
          loop (EXP2ind pf | j2)
      end
  in
    if i = one then
      @(EXP2bas () | one)
    else
      loop (EXP2bas () | one)
  end

(*------------------------------------------------------------------*)

stadef link (ifirst : int, ilast : int, i : int) : bool =
  0 <= i && i <= ilast - ifirst + 1

typedef link_t (ifirst : int, ilast : int, i : int) =
  (* A size_t within legal range for a normalized link, including the
     "nil" link 0. *)
  [link (ifirst, ilast, i)]
  size_t i
typedef link_t (ifirst : int, ilast : int) =
  [i : int]
  link_t (ifirst, ilast, i)

fn {a : [email protected]}
find_pile {ifirst, ilast : int | ifirst <= ilast}
          {n             : int | ilast < n}
          {num_piles     : nat | num_piles <= ilast - ifirst + 1}
          {n_piles       : int | ilast - ifirst + 1 <= n_piles}
          {q             : pos | q <= ilast - ifirst + 1}
          (ifirst        : size_t ifirst,
           arr           : &RD(array (a, n)),
           num_piles     : size_t num_piles,
           piles         : &RD(array (link_t (ifirst, ilast),
                               n_piles)),
           q             : size_t q)
    :<> [i : pos | i <= num_piles + 1]
        size_t i =
  (*
    Bottenbruch search for the leftmost pile whose top is greater than
    or equal to the next value dealt by "deal".

    References:

      * H. Bottenbruch, "Structure and use of ALGOL 60", Journal of
        the ACM, Volume 9, Issue 2, April 1962, pp.161-221.
        https://doi.org/10.1145/321119.321120

        The general algorithm is described on pages 214 and 215.

      * https://en.wikipedia.org/w/index.php?title=Binary_search_algorithm&oldid=1062988272#Alternative_procedure
  *)
  if num_piles = i2sz 0 then
    i2sz 1
  else
    let
      macdef lt = patience_sort$lt

      prval () = lemma_g1uint_param ifirst
      prval () = prop_verify {0 <= ifirst} ()

      fun
      loop {j, k  : nat | j <= k; k < num_piles}
           .<k - j>.
           (arr   : &RD(array (a, n)),
            piles : &array (link_t (ifirst, ilast), n_piles),
            j     : size_t j,
            k     : size_t k)
          :<> [i : pos | i <= num_piles + 1]
              size_t i =
        if j = k then
          begin
            if succ j <> num_piles then
              succ j
            else
              let
                val piles_j = piles[j]
                val () = $effmask_exn assertloc (piles_j <> g1u2u 0u)

                val x1 = arr[pred q + ifirst]
                and x2 = arr[pred piles_j + ifirst]
              in
                if x2 \lt x1 then
                  succ (succ j)
                else
                  succ j
              end
          end
        else
          let
            typedef index (i : int) = [0 <= i; i < n] size_t i
            typedef index = [i : int] index i

            stadef i = j + ((k - j) / 2)
            val i : size_t i = j + ((k - j) / g1u2u 2u)

            val piles_j = piles[j]
            val () = $effmask_exn assertloc (piles_j <> g1u2u 0u)

            val x1 = arr[pred q + ifirst]
            and x2 = arr[pred piles_j + ifirst]
          in
            if x2 \lt x1 then
              loop (arr, piles, i + 1, k)
            else
              loop (arr, piles, j, i)
          end
    in
      loop (arr, piles, g1u2u 0u, pred num_piles)
    end

fn {a : [email protected]}
deal {ifirst, ilast : int | ifirst <= ilast}
     {n             : int | ilast < n}
     (ifirst : size_t ifirst,
      ilast  : size_t ilast,
      arr    : &RD(array (a, n)))
    :<!wrt> [num_piles   : int | num_piles <= ilast - ifirst + 1]
            [n_piles     : int | ilast - ifirst + 1 <= n_piles]
            [n_links     : int | ilast - ifirst + 1 <= n_links]
            [p_piles     : addr]
            [p_links     : addr]
            @(size_t num_piles,
              array_tup_vt (link_t (ifirst, ilast),
                            p_piles, n_piles),
              array_tup_vt (link_t (ifirst, ilast),
                            p_links, n_links)) =
  let
    prval () = prop_verify {0 < ilast - ifirst + 1} ()

    stadef num_elems = ilast - ifirst + 1
    val num_elems : size_t num_elems = succ (ilast - ifirst)

    typedef link_t (i : int) = link_t (ifirst, ilast, i)
    typedef link_t = link_t (ifirst, ilast)

    val zero : size_t 0 = g1u2u 0u
    val one : size_t 1 = g1u2u 1u
    val link_nil : link_t 0 = g1u2u 0u

    fun
    loop {q         : pos | q <= num_elems + 1}
         {m         : nat | m <= num_elems}
         .<num_elems + 1 - q>.
         (arr       : &RD(array (a, n)),
          q         : size_t q,
          piles     : &array (link_t, num_elems),
          links     : &array (link_t, num_elems),
          m         : size_t m)
        :<!wrt> [num_piles : nat | num_piles <= num_elems]
                size_t num_piles =
      if q = succ (num_elems) then
        m
      else
        let
          val i = find_pile {ifirst, ilast} (ifirst, arr, m, piles, q)

          (* We have no proof the number of elements will not exceed
             storage. However, we know it will not, because the number
             of piles cannot exceed the size of the input. Let us get
             a "proof" by runtime check. *)
          val () = $effmask_exn assertloc (i <= num_elems)
        in
          links[pred q] := piles[pred i];
          piles[pred i] := q;
          if i = succ m then
            loop {q + 1} (arr, succ q, piles, links, succ m)
          else
            loop {q + 1} (arr, succ q, piles, links, m)
        end

    val piles_tup = array_ptr_alloc<link_t> num_elems
    macdef piles = !(piles_tup.2)
    val () = array_initize_elt<link_t> (piles, num_elems, link_nil)

    val links_tup = array_ptr_alloc<link_t> num_elems
    macdef links = !(links_tup.2)
    val () = array_initize_elt<link_t> (links, num_elems, link_nil)

    val num_piles = loop (arr, one, piles, links, zero)
  in
    @(num_piles, piles_tup, links_tup)
  end

fn {a : [email protected]}
k_way_merge {ifirst, ilast : int | ifirst <= ilast}
            {n             : int | ilast < n}
            {n_piles       : int | ilast - ifirst + 1 <= n_piles}
            {num_piles     : pos | num_piles <= ilast - ifirst + 1}
            {n_links       : int | ilast - ifirst + 1 <= n_links}
            (ifirst        : size_t ifirst,
             ilast         : size_t ilast,
             arr           : &RD(array (a, n)),
             num_piles     : size_t num_piles,
             piles         : &array (link_t (ifirst, ilast), n_piles),
             links         : &array (link_t (ifirst, ilast), n_links))
    :<!wrt> (* Return an array of indices into arr. *)
            [p : addr]
            array_tup_vt
              ([i : int | ifirst <= i; i <= ilast] size_t i,
               p, ilast - ifirst + 1) =
  (*
    k-way merge by tournament tree.

    See Knuth, volume 3, and also
    https://en.wikipedia.org/w/index.php?title=K-way_merge_algorithm&oldid=1047851465#Tournament_Tree

    However, I store a winners tree instead of the recommended losers
    tree. If the tree were stored as linked nodes, it would probably
    be more efficient to store a losers tree. However, I am storing
    the tree as an array, and one can find an opponent quickly by
    simply toggling the least significant bit of a competitor's array
    index.
  *)
  let
    typedef link_t (i : int) = link_t (ifirst, ilast, i)
    typedef link_t = [i : int] link_t i

    val link_nil : link_t 0 = g1u2u 0u

    typedef index_t (i : int) = [ifirst <= i; i <= ilast] size_t i
    typedef index_t = [i : int] index_t i

    val [total_external_nodes : int]
        @(_ | total_external_nodes) = next_power_of_two num_piles
    prval () = prop_verify {num_piles <= total_external_nodes} ()

    stadef total_nodes = (2 * total_external_nodes) - 1
    val total_nodes : size_t total_nodes =
      pred (g1u2u 2u * total_external_nodes)

    (* We will ignore index 0 of the winners tree arrays. *)
    stadef winners_size = total_nodes + 1
    val winners_size : size_t winners_size = succ total_nodes

    val winners_values_tup = array_ptr_alloc<link_t> winners_size
    macdef winners_values = !(winners_values_tup.2)
    val () = array_initize_elt<link_t> (winners_values, winners_size,
                                        link_nil)

    val winners_links_tup = array_ptr_alloc<link_t> winners_size
    macdef winners_links = !(winners_links_tup.2)
    val () = array_initize_elt<link_t> (winners_links, winners_size,
                                        link_nil)

    (* - - - - - - - - - - - - - - - - - - - - - - - - - - *)
    (* Record which pile a winner will have come from.     *)

    fun
    init_pile_links
              {i : nat | i <= num_piles}
              .<num_piles - i>.
              (winners_links : &array (link_t, winners_size),
               i             : size_t i)
        :<!wrt> void =
      if i <> num_piles then
        begin
          winners_links[total_external_nodes + i] := succ i;
          init_pile_links (winners_links, succ i)
        end

    val () = init_pile_links (winners_links, g1u2u 0u)

    (* - - - - - - - - - - - - - - - - - - - - - - - - - - *)
    (* The top of each pile becomes a starting competitor. *)

    fun
    init_competitors
              {i : nat | i <= num_piles}
              .<num_piles - i>.
              (winners_values : &array (link_t, winners_size),
               piles          : &array (link_t, n_piles),
               i              : size_t i)
        :<!wrt> void =
      if i <> num_piles then
        begin
          winners_values[total_external_nodes + i] := piles[i];
          init_competitors (winners_values, piles, succ i)
        end

    val () = init_competitors (winners_values, piles, g1u2u 0u)

    (* - - - - - - - - - - - - - - - - - - - - - - - - - - *)
    (* Discard the top of each pile.                       *)

    fun
    discard_tops {i : nat | i <= num_piles}
                 .<num_piles - i>.
                 (piles : &array (link_t, n_piles),
                  links : &array (link_t, n_links),
                  i     : size_t i)
        :<!wrt> void =
      if i <> num_piles then
        let
          val link = piles[i]

          (* None of the piles should have been empty. *)
          val () = $effmask_exn assertloc (link <> g1u2u 0u)
        in
          piles[i] := links[pred link];
          discard_tops (piles, links, succ i)
        end

    val () = discard_tops (piles, links, g1u2u 0u)

    (* - - - - - - - - - - - - - - - - - - - - - - - - - - *)
    (* How to play a game.                                 *)

    fn
    play_game {i              : int | 2 <= i; i <= total_nodes}
              (arr            : &RD(array (a, n)),
               winners_values : &array (link_t, winners_size),
               i              : size_t i)
        :<> [iwinner : pos | iwinner <= total_nodes]
            size_t iwinner =
      let
        macdef lt = patience_sort$lt

        fn
        find_opponent {i : int | 2 <= i; i <= total_nodes}
                      (i : size_t i)
            :<> [j : int | 2 <= j; j <= total_nodes]
                size_t j =
          let
            (* The prelude contains bitwise operations only for
               non-dependent unsigned integer. We will not bother to
               add them ourselves, but instead go back and forth
               between dependent and non-dependent. *)
            val i0 = g0ofg1 i
            val j0 = g0uint_lxor<size_kind> (i0, g0u2u 1u)
            val j = g1ofg0 j0

            (* We have no proof the opponent is in the proper
               range. Create a "proof" by runtime checks. *)
            val () = $effmask_exn assertloc (g1u2u 2u <= j)
            val () = $effmask_exn assertloc (j <= total_nodes)
          in
            j
          end

        val j = find_opponent i
        val winner_i = winners_values[i]
        and winner_j = winners_values[j]
      in
        if winner_i = link_nil then
          j
        else if winner_j = link_nil then
          i
        else
          let
            val i1 = pred winner_i + ifirst
            and i2 = pred winner_j + ifirst
            prval () = lemma_g1uint_param i1
            prval () = lemma_g1uint_param i2
          in
            if arr[i2] \lt arr[i1] then j else i
          end
      end

    (* - - - - - - - - - - - - - - - - - - - - - - - - - - *)

    fun
    build_tree {istart : pos | istart <= total_external_nodes}
               .<istart>.
               (arr            : &RD(array (a, n)),
                winners_values : &array (link_t, winners_size),
                winners_links  : &array (link_t, winners_size),
                istart         : size_t istart)
        :<!wrt> void =
      if istart <> 1 then
        let
          fun
          play_initial_games
                    {i : int | istart <= i; i <= (2 * istart) + 1}
                    .<(2 * istart) + 1 - i>.
                    (arr            : &RD(array (a, n)),
                     winners_values : &array (link_t, winners_size),
                     winners_links  : &array (link_t, winners_size),
                     i              : size_t i)
              :<!wrt> void =
            if i <= pred (istart + istart) then
              let
                val iwinner = play_game (arr, winners_values, i)
                and i2 = i / g1u2u 2u
              in
                winners_values[i2] := winners_values[iwinner];
                winners_links[i2] := winners_links[iwinner];
                play_initial_games (arr, winners_values,
                                    winners_links, succ (succ i))
              end
        in
          play_initial_games (arr, winners_values, winners_links,
                              istart);
          build_tree (arr, winners_values, winners_links,
                      istart / g1u2u 2u)
        end

    val () = build_tree (arr, winners_values, winners_links,
                         total_external_nodes)

    (* - - - - - - - - - - - - - - - - - - - - - - - - - - *)

    fun
    replay_games {i : pos | i <= total_nodes}
                 ..
                 (arr            : &RD(array (a, n)),
                  winners_values : &array (link_t, winners_size),
                  winners_links  : &array (link_t, winners_size),
                  i              : size_t i)
        :<!wrt> void =
      if i <> g1u2u 1u then
        let
          val iwinner = play_game (arr, winners_values, i)
          and i2 = i / g1u2u 2u
        in
          winners_values[i2] := winners_values[iwinner];
          winners_links[i2] := winners_links[iwinner];
          replay_games (arr, winners_values, winners_links, i2)
        end

    stadef num_elems = ilast - ifirst + 1
    val num_elems : size_t num_elems = succ (ilast - ifirst)

    val sorted_tup = array_ptr_alloc<index_t> num_elems

    fun
    merge {isorted  : nat | isorted <= num_elems}
          {p_sorted : addr}
          .<num_elems - isorted>.
          (pf_sorted      : !array_v (index_t?, p_sorted,
                                      num_elems - isorted)
                                >> array_v (index_t, p_sorted,
                                            num_elems - isorted) |
           arr            : &RD(array (a, n)),
           piles          : &array (link_t, n_piles),
           links          : &array (link_t, n_links),
           winners_values : &array (link_t, winners_size),
           winners_links  : &array (link_t, winners_size),
           p_sorted       : ptr p_sorted,
           isorted        : size_t isorted)
        :<!wrt> void =
      (* This function not only fills in the "sorted_tup" array, but
         transforms it from "uninitialized" to "initialized". *)
      if isorted <> num_elems then
        let
          prval @(pf_elem, pf_rest) = array_v_uncons pf_sorted
          val winner = winners_values[1]
          val () = $effmask_exn assertloc (winner <> link_nil)
          val () = !p_sorted := pred winner + ifirst

          (* Move to the next element in the winner's pile. *)
          val ilink = winners_links[1]
          val () = $effmask_exn assertloc (ilink <> link_nil)
          val inext = piles[pred ilink]
          val () = (if inext <> link_nil then
                      piles[pred ilink] := links[pred inext])

          (* Replay games, with the new element as a competitor. *)
          val i = (total_nodes / g1u2u 2u) + ilink
          val () = $effmask_exn assertloc (i <= total_nodes)
          val () = winners_values[i] := inext
          val () =
            replay_games (arr, winners_values, winners_links, i)

          val () = merge (pf_rest | arr, piles, links,
                                    winners_values, winners_links,
                                    ptr_succ<index_t> p_sorted,
                                    succ isorted)
          prval () = pf_sorted := array_v_cons (pf_elem, pf_rest)
        in
        end
      else
        let
          prval () = pf_sorted :=
            array_v_unnil_nil{index_t?, index_t} pf_sorted
        in
        end

    val () = merge (sorted_tup.0 | arr, piles, links,
                                   winners_values, winners_links,
                                   sorted_tup.2, i2sz 0)

    val () = array_ptr_free (winners_values_tup.0,
                             winners_values_tup.1 |
                             winners_values_tup.2)
    val () = array_ptr_free (winners_links_tup.0,
                             winners_links_tup.1 |
                             winners_links_tup.2)
  in
    sorted_tup
  end

implement {a}
patience_sort (arr, ifirst, len) =
  let
    prval () = lemma_g1uint_param ifirst
    prval () = lemma_g1uint_param len
  in
    if len = i2sz 0 then
      let
        val sorted_tup = array_ptr_alloc<size_t 0> len
        prval () = sorted_tup.0 :=
          array_v_unnil_nil{Size_t?, Size_t} sorted_tup.0
      in
        sorted_tup
      end
    else
      let
        val ilast = ifirst + pred len
        val @(num_piles, piles_tup, links_tup) =
          deal (ifirst, ilast, arr)
        macdef piles = !(piles_tup.2)
        macdef links = !(links_tup.2)
        prval () = lemma_g1uint_param num_piles
        val () = $effmask_exn assertloc (num_piles <> i2sz 0)
        val sorted_tup = k_way_merge (ifirst, ilast, arr,
                                         num_piles, piles, links)
      in
        array_ptr_free (piles_tup.0, piles_tup.1 | piles_tup.2);
        array_ptr_free (links_tup.0, links_tup.1 | links_tup.2);
        sorted_tup
      end
  end

(*------------------------------------------------------------------*)

fn
int_patience_sort_ascending
          {ifirst, len   : int | 0 <= ifirst}
          {n             : int | ifirst + len <= n}
          (arr           : &RD(array (int, n)),
           ifirst        : size_t ifirst,
           len           : size_t len)
    :<!wrt> [p : addr]
            array_tup_vt
              ([i : int | len == 0 ||
                          (ifirst <= i && i < ifirst + len)] size_t i,
               p, len) =
  let
    implement
    patience_sort$lt<int> (x, y) =
      x < y
  in
    patience_sort<int> (arr, ifirst, len)
  end

fn {a : [email protected]}
find_length {n   : int}
            (lst : list (a, n))
    :<> [m : int | m == n] size_t m =
  let
    prval () = lemma_list_param lst
  in
    g1i2u (length lst)
  end

implement
main0 () =
  let
    val example_list =
      $list (22, 15, 98, 82, 22, 4, 58, 70, 80, 38, 49, 48, 46, 54,
             93, 8, 54, 2, 72, 84, 86, 76, 53, 37, 90)

    val ifirst = i2sz 10
    val [len : int] len = find_length example_list

    #define ARRSZ 100
    val () = assertloc (i2sz 10 + len <= ARRSZ)

    var arr : array (int, ARRSZ)
    val () = array_initize_elt<int> (arr, i2sz ARRSZ, 0)

    prval @(pf_left, pf_right) =
      array_v_split {int} {..} {ARRSZ} {10} ([email protected] arr)
    prval @(pf_middle, pf_right) =
      array_v_split {int} {..} {90} {len} pf_right

    val p = ptr_add<int> ([email protected] arr, 10)
    val () = array_copy_from_list<int> (!p, example_list)

    prval pf_right = array_v_unsplit (pf_middle, pf_right)
    prval () = [email protected] arr := array_v_unsplit (pf_left, pf_right)

    val @(pf_sorted, pfgc_sorted | p_sorted) =
      int_patience_sort_ascending (arr, i2sz 10, len)

    macdef sorted = !p_sorted

    var i : [i : nat | i <= len] size_t i
  in
    print! ("unsorted  ");
    for (i := i2sz 0; i <> len; i := succ i)
      print! (" ", arr[i2sz 10 + i]);
    println! ();

    print! ("sorted    ");
    for (i := i2sz 0; i <> len; i := succ i)
      print! (" ", arr[sorted[i]]);
    println! ();

    array_ptr_free (pf_sorted, pfgc_sorted | p_sorted)
  end

(*------------------------------------------------------------------*)


PatienceSort(A){
    P:=0, Pile:=[], Result:=[]
    for k, v in A
    {
        Pushed := 0
        loop % P
        {
            i := A_Index
            if Pile[i].Count() && (Pile[i, 1] >= v)
            {
                Pile[i].InsertAt(1, v)
                pushed := true
                break
            }
        }
        if Pushed
            continue
        P++
        Pile[p] := []
        Pile[p].InsertAt(1, v)
    }

    ; optional to show steps ;;;;;;;;;;;;;;;;;;;;;;;
    loop % P
    {
        i := A_Index, step := ""
        for k, v in Pile[i]
            step .= v ", "
        step := "Pile" i " = "  Trim(step, ", ")
        steps .= step "`n"
    }
    MsgBox % steps
    ; end optional ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    loop % A.Count()
    {
        Collect:=[]
        loop % P
            if Pile[A_index].Count()
                Collect.Push(Pile[A_index, 1])

        for k, v in Collect
            if k=1
                m := v
            else if (v < m)
            {
                m := v
                break
            }

        Result.push(m)
        loop % P
            if (m = Pile[A_index, 1])
            {
                Pile[A_index].RemoveAt(1)
                break
            }
    }
    return Result
}


#include<stdlib.h>
#include<stdio.h>

int* patienceSort(int* arr,int size){
	int decks[size][size],i,j,min,pickedRow;

	int *count = (int*)calloc(sizeof(int),size),*sortedArr = (int*)malloc(size*sizeof(int));

	for(i=0;i<size;i++){
		for(j=0;j<size;j++){
			if(count[j]==0 || (count[j]>0 && decks[j][count[j]-1]>=arr[i])){
				decks[j][count[j]] = arr[i];
				count[j]++;
				break;
			}
		}
	}

	min = decks[0][count[0]-1];
	pickedRow = 0;

	for(i=0;i<size;i++){
		for(j=0;j<size;j++){
			if(count[j]>0 && decks[j][count[j]-1]<min){
				min = decks[j][count[j]-1];
				pickedRow = j;
			}
		}
		sortedArr[i] = min;
		count[pickedRow]--;

		for(j=0;j<size;j++)
			if(count[j]>0){
				min = decks[j][count[j]-1];
				pickedRow = j;
				break;
			}
	}

	free(count);
	free(decks);

	return sortedArr;
}

int main(int argC,char* argV[])
{
	int *arr, *sortedArr, i;

	if(argC==0)
		printf("Usage : %s <integers to be sorted separated by space>");
	else{
		arr = (int*)malloc((argC-1)*sizeof(int));

		for(i=1;i<=argC;i++)
			arr[i-1] = atoi(argV[i]);

		sortedArr = patienceSort(arr,argC-1);

		for(i=0;i<argC-1;i++)
			printf("%d ",sortedArr[i]);
	}

	return 0;
}


#include <iostream>
#include <vector>
#include <stack>
#include <iterator>
#include <algorithm>
#include <cassert>

template <class E>
struct pile_less {
  bool operator()(const std::stack<E> &pile1, const std::stack<E> &pile2) const {
    return pile1.top() < pile2.top();
  }
};

template <class E>
struct pile_greater {
  bool operator()(const std::stack<E> &pile1, const std::stack<E> &pile2) const {
    return pile1.top() > pile2.top();
  }
};

template <class Iterator>
void patience_sort(Iterator first, Iterator last) {
  typedef typename std::iterator_traits<Iterator>::value_type E;
  typedef std::stack<E> Pile;

  std::vector<Pile> piles;
  // sort into piles
  for (Iterator it = first; it != last; it++) {
    E& x = *it;
    Pile newPile;
    newPile.push(x);
    typename std::vector<Pile>::iterator i =
      std::lower_bound(piles.begin(), piles.end(), newPile, pile_less<E>());
    if (i != piles.end())
      i->push(x);
    else
      piles.push_back(newPile);
  }

  // priority queue allows us to merge piles efficiently
  // we use greater-than comparator for min-heap
  std::make_heap(piles.begin(), piles.end(), pile_greater<E>());
  for (Iterator it = first; it != last; it++) {
    std::pop_heap(piles.begin(), piles.end(), pile_greater<E>());
    Pile &smallPile = piles.back();
    *it = smallPile.top();
    smallPile.pop();
    if (smallPile.empty())
      piles.pop_back();
    else
      std::push_heap(piles.begin(), piles.end(), pile_greater<E>());
  }
  assert(piles.empty());
}

int main() {
  int a[] = {4, 65, 2, -31, 0, 99, 83, 782, 1};
  patience_sort(a, a+sizeof(a)/sizeof(*a));
  std::copy(a, a+sizeof(a)/sizeof(*a), std::ostream_iterator<int>(std::cout, ", "));
  std::cout << std::endl;
  return 0;
}


(defn patience-insert
  "Inserts a value into the sequence where each element is a stack.
   Comparison replaces the definition of less than.
   Uses the greedy strategy."
  [comparison sequence value]
  (lazy-seq
   (if (empty? sequence) `((~value)) ;; If there are no places to put the "card", make a new stack
       (let [stack (first sequence)
             top       (peek stack)]
         (if (comparison value top)
           (cons (conj stack value)  ;; Either put the card in a stack or recurse to the next stack
                 (rest sequence))
           (cons stack
                 (patience-insert comparison
                                  (rest sequence)
                                  value)))))))

(defn patience-remove
  "Removes the value from the top of the first stack it shows up in.
   Leaves the stacks otherwise intact."
  [sequence value]
  (lazy-seq
   (if (empty? sequence) nil              ;; If there are no stacks, we have no work to do
       (let [stack (first sequence)
             top       (peek stack)]
         (if (= top value)                ;; Are we there yet?
           (let [left-overs (pop stack)]
             (if (empty? left-overs)      ;; Handle the case that the stack is empty and needs to be removed
               (rest sequence)
               (cons left-overs
                     (rest sequence))))
           (cons stack
                 (patience-remove (rest sequence)
                                  value)))))))

(defn patience-recover
  "Builds a sorted sequence from a list of patience stacks.
   The given comparison takes the place of 'less than'"
  [comparison sequence]
  (loop [sequence sequence
         sorted         []]
    (if (empty? sequence) sorted
        (let [smallest  (reduce #(if (comparison %1 %2) %1 %2)  ;; Gets the smallest element in the list
                                (map peek sequence))
              remaining    (patience-remove sequence smallest)]
          (recur remaining
                 (conj sorted smallest)))))) ;; Recurse over the remaining values and add the new smallest to the end of the sorted list

(defn patience-sort
  "Sorts the sequence by comparison.
   First builds the list of valid patience stacks.
   Then recovers the sorted list from those.
   If you don't supply a comparison, assumes less than."
  ([comparison sequence]
     (->> (reduce (comp doall ;; This is prevent a stack overflow by making sure all work is done when it needs to be
                        (partial patience-insert comparison)) ;; Insert all the values into the list of stacks
                  nil
                  sequence)
          (patience-recover comparison)))              ;; After we have the stacks, send it off to recover the sorted list
  ([sequence]
     ;; In the case we don't have an operator, defer to ourselves with less than
     (patience-sort < sequence)))

;; Sort the test sequence and print it
(println (patience-sort [4 65 2 -31 0 99 83 782 1]))


import std.stdio, std.array, std.range, std.algorithm;

void patienceSort(T)(T[] items) /*pure nothrow @safe*/
if (__traits(compiles, T.init < T.init)) {
    //SortedRange!(int[][], q{ a.back < b.back }) piles;
    T[][] piles;

    foreach (x; items) {
        auto p = [x];
        immutable i = piles.length -
                      piles
                      .assumeSorted!q{ a.back < b.back }
                      .upperBound(p)
                      .length;
        if (i != piles.length)
            piles[i] ~= x;
        else
            piles ~= p;
    }

    piles.nWayUnion!q{ a > b }.copy(items.retro);
}

void main() {
    auto data = [4, 65, 2, -31, 0, 99, 83, 782, 1];
    data.patienceSort;
    assert(data.isSorted);
    data.writeln;
}


defmodule Sort do
  def patience_sort(list) do
    piles = deal_pile(list, [])
    merge_pile(piles, [])
  end

  defp deal_pile([], piles), do: piles
  defp deal_pile([h|t], piles) do
    index = Enum.find_index(piles, fn pile -> hd(pile) <= h end)
    new_piles = if index, do:   add_element(piles, index, h, []),
                          else: piles ++ [[h]]
    deal_pile(t, new_piles)
  end

  defp add_element([h|t], 0,     elm, work), do: Enum.reverse(work, [[elm | h] | t])
  defp add_element([h|t], index, elm, work), do: add_element(t, index-1, elm, [h | work])

  defp merge_pile([], list), do: list
  defp merge_pile(piles, list) do
    {max, index} = max_index(piles)
    merge_pile(delete_element(piles, index, []), [max | list])
  end

  defp max_index([h|t]), do: max_index(t, hd(h), 1, 0)

  defp max_index([], max, _, max_i), do: {max, max_i}
  defp max_index([h|t], max, index, _) when hd(h)>max, do: max_index(t, hd(h), index+1, index)
  defp max_index([_|t], max, index, max_i)           , do: max_index(t, max, index+1, max_i)

  defp delete_element([h|t], 0, work) when length(h)==1, do: Enum.reverse(work, t)
  defp delete_element([h|t], 0, work)                  , do: Enum.reverse(work, [tl(h) | t])
  defp delete_element([h|t], index, work), do: delete_element(t, index-1, [h | work])
end

IO.inspect Sort.patience_sort [4, 65, 2, -31, 0, 99, 83, 782, 1]


Warning: trampoline generated for nested function "less" [-Wtrampolines]


package main

import (
  "fmt"
  "container/heap"
  "sort"
)

type IntPile []int
func (self IntPile) Top() int { return self[len(self)-1] }
func (self *IntPile) Pop() int {
    x := (*self)[len(*self)-1]
    *self = (*self)[:len(*self)-1]
    return x
}

type IntPilesHeap []IntPile
func (self IntPilesHeap) Len() int { return len(self) }
func (self IntPilesHeap) Less(i, j int) bool { return self[i].Top() < self[j].Top() }
func (self IntPilesHeap) Swap(i, j int) { self[i], self[j] = self[j], self[i] }
func (self *IntPilesHeap) Push(x interface{}) { *self = append(*self, x.(IntPile)) }
func (self *IntPilesHeap) Pop() interface{} {
    x := (*self)[len(*self)-1]
    *self = (*self)[:len(*self)-1]
    return x
}

func patience_sort (n []int) {
  var piles []IntPile
  // sort into piles
  for _, x := range n {
    j := sort.Search(len(piles), func (i int) bool { return piles[i].Top() >= x })
    if j != len(piles) {
      piles[j] = append(piles[j], x)
    } else {
      piles = append(piles, IntPile{ x })
    }
  }

  // priority queue allows us to merge piles efficiently
  hp := IntPilesHeap(piles)
  heap.Init(&hp)
  for i, _ := range n {
    smallPile := heap.Pop(&hp).(IntPile)
    n[i] = smallPile.Pop()
    if len(smallPile) != 0 {
      heap.Push(&hp, smallPile)
    }
  }
  if len(hp) != 0 {
    panic("something went wrong")
  }
}

func main() {
    a := []int{4, 65, 2, -31, 0, 99, 83, 782, 1}
    patience_sort(a)
    fmt.Println(a)
}


import Control.Monad.ST
import Control.Monad
import Data.Array.ST
import Data.List
import qualified Data.Set as S

newtype Pile a = Pile [a]

instance Eq a => Eq (Pile a) where
  Pile (x:_) == Pile (y:_) = x == y

instance Ord a => Ord (Pile a) where
  Pile (x:_) `compare` Pile (y:_) = x `compare` y

patienceSort :: Ord a => [a] -> [a]
patienceSort = mergePiles . sortIntoPiles where

  sortIntoPiles :: Ord a => [a] -> [[a]]
  sortIntoPiles lst = runST $ do
      piles <- newSTArray (1, length lst) []
      let bsearchPiles x len = aux 1 len where
            aux lo hi | lo > hi = return lo
                      | otherwise = do
              let mid = (lo + hi) `div` 2
              m <- readArray piles mid
              if head m < x then
                aux (mid+1) hi
              else
                aux lo (mid-1)
          f len x = do
            i <- bsearchPiles x len
            writeArray piles i . (x:) =<< readArray piles i
            return $ if i == len+1 then len+1 else len
      len <- foldM f 0 lst
      e <- getElems piles
      return $ take len e
      where newSTArray :: Ix i => (i,i) -> e -> ST s (STArray s i e)
            newSTArray = newArray

  mergePiles :: Ord a => [[a]] -> [a]
  mergePiles = unfoldr f . S.fromList . map Pile where
    f pq = case S.minView pq of
             Nothing -> Nothing
             Just (Pile [x], pq') -> Just (x, pq')
             Just (Pile (x:xs), pq') -> Just (x, S.insert (Pile xs) pq')

main :: IO ()
main = print $ patienceSort [4, 65, 2, -31, 0, 99, 83, 782, 1]


#---------------------------------------------------------------------
#
# Patience sorting.
#

procedure patience_sort (less, lst)
  local piles

  piles := deal (less, lst)
  return k_way_merge (less, piles)
end

procedure deal (less, lst)
  local piles
  local x
  local i

  piles := []
  every x := !lst do {
    i := find_pile (less, x, piles)
    if i = *piles + 1 then {
      # Start a new pile after the existing ones.
      put (piles, [x])
    } else {
      # Push the new value onto the top of an existing pile.
      push (piles[i], x)
    }
  }
  return piles
end

procedure find_pile (less, x, piles)
  local i, j, k

  #
  # Do a Bottenbruch search for the leftmost pile whose top is greater
  # than or equal to x. Return an index such that:
  #
  #   * if x is greater than the top element at the far right, then
  #     the index returned will be num-piles.
  #
  #   * otherwise, x is greater than every top element to the left of
  #     index, and less than or equal to the top elements at index and
  #     to the right of index.
  #
  # References:
  #
  #   * H. Bottenbruch, "Structure and use of ALGOL 60", Journal of
  #     the ACM, Volume 9, Issue 2, April 1962, pp.161-221.
  #     https://doi.org/10.1145/321119.321120
  #
  #     The general algorithm is described on pages 214 and 215.
  #
  #   * https://en.wikipedia.org/w/index.php?title=Binary_search_algorithm&oldid=1062988272#Alternative_procedure
  #

  j := 0
  k := *piles - 1
  until j = k do {
    i := (j + k) / 2
    if less (piles[j + 1][1], x) then {
      j := i + 1
    } else {
      k := i
    }
  }
  if j = *piles - 1 & less (piles[j + 1][1], x) then {
    # We need a new pile.
    j +:= 1
  }
  return j + 1
end

#---------------------------------------------------------------------
#
# k-way merge by tournament tree.
#
# See Knuth, volume 3, and also
# https://en.wikipedia.org/w/index.php?title=K-way_merge_algorithm&oldid=1047851465#Tournament_Tree
#
# However, I store a winners tree instead of the recommended losers
# tree. If the tree were stored as linked nodes, it would probably be
# more efficient to store a losers tree. However, I am storing the
# tree as an Icon list, and one can find an opponent quickly by simply
# toggling the least significant bit of a competitor's array index.
#

record infinity ()

procedure is_infinity (x)
  return type (x) == "infinity"
end

procedure k_way_merge (less, lists)
  local merged_list

  # Return the merge as a list, which is guaranteed to be freshly
  # allocated.

  every put (merged_list := [], generate_k_way_merge (less, lists))
  return merged_list
end

procedure generate_k_way_merge (less, lists)

  # Generate the results of the merge.

  case *lists of {
    0 : fail
    1 : every suspend !(lists[1])
    default : every suspend generate_merged_lists (less, lists)
  }
end

procedure generate_merged_lists (less, lists)
  local indices
  local winners
  local winner, winner_index
  local i
  local next_value

  indices := list (*lists, 2)
  winners := build_tree (less, lists)
  until is_infinity (winners[1][1]) do {
    suspend winners[1][1]
    winner_index := winners[1][2]
    next_value := get_next (lists, indices, winner_index)
    i := ((*winners + 1) / 2) + winner_index - 1
    winners[i] := [next_value, winner_index]
    replay_games (less, winners, i)
  }
end

procedure build_tree (less, lists)
  local total_external_nodes
  local total_nodes
  local winners
  local i, j
  local istart
  local i1, i2
  local elem1, elem2
  local iwinner, winner

  total_external_nodes := next_power_of_two (*lists)
  total_nodes := (2 * total_external_nodes) - 1
  winners := list (total_nodes)
  every i := 1 to total_external_nodes do {
    j := total_external_nodes + (i - 1)
    if *lists < i | *(lists[i]) = 0 then {
      winners[j] := [infinity (), i]
    } else {
      winners[j] := [lists[i][1], i]
    }
  }
  istart := total_external_nodes
  while istart ~= 1 do {
    every i := istart to (2 * istart) - 1 by 2 do {
      i1 := i
      i2 := ixor (i, 1)
      elem1 := winners[i1][1]
      elem2 := winners[i2][1]
      iwinner := (if play_game (less, elem1, elem2) then i1 else i2)
      winner := winners[iwinner]
      winners[i / 2] := winner
    }
    istart /:= 2
  }
  return winners
end

procedure replay_games (less, winners, i)
  local i1, i2
  local elem1, elem2
  local iwinner, winner

  until i = 1 do {
    i1 := i
    i2 := ixor (i1, 1)
    elem1 := winners[i1][1]
    elem2 := winners[i2][1]
    iwinner := (if play_game (less, elem1, elem2) then i1 else i2)
    winner := winners[iwinner]
    i /:= 2
    winners[i] := winner
  }
  return
end

procedure play_game (less, x, y)
  if is_infinity (x) then fail
  if is_infinity (y) then return
  if less (y, x) then fail
  return
end

procedure get_next (lists, indices, i)
  local next_value

  if *(lists[i]) < indices[i] then {
    next_value := infinity ()
  } else {
    next_value := lists[i][indices[i]]
    indices[i] +:= 1
  }
  return next_value
end

procedure next_power_of_two (n)
  local i

  # This need not be a fast implementation. Also, it need not return
  # any value less than 2; a single list requires no merge.
  i := 2
  while i < n do i +:= i
  return i
end

#---------------------------------------------------------------------

procedure main ()
  local example_numbers

  example_numbers := [22, 15, 98, 82, 22, 4, 58, 70, 80, 38, 49, 48,
                      46, 54, 93, 8, 54, 2, 72, 84, 86, 76, 53, 37,
                      90]

  writes ("unsorted  ")
  every writes (" ", !example_numbers)
  write ()
  writes ("sorted    ")
  every writes (" ", !patience_sort ("<", example_numbers))
  write ()
end

#---------------------------------------------------------------------


Until =: 2 :'u^:(0=v)^:_'
Filter =: (#~`)(`:6)

locate_for_append =: 1 i.~ (<&> {:S:0)  NB. returns an index
append =: (<@:(({::~ >:) , 0 {:: [)`]`(}[email protected]:[)}) :: [
pile =: (,  append locate_for_append)/@:(;/)  NB. pile DATA

smallest =: ((>:@:i. , ]) <./)@:({:S:0@:}.) NB. index of pile with smallest value , that value
transfer =: (}:&.>@:({~ {.) , <@:((0{::[),{:@:]))`(1 0 * ])`[}
unpile =: >@:{[email protected]:((0<#S:0)[email protected]:(transfer smallest)Until(1=#))@:(a:&,)

patience_sort =: [email protected]:pile

assert (/:~ -: patience_sort) [email protected]$~30    NB. test with 30 randomly chosen integers

Show =: 1 : 0
 smoutput y
 u y
:
 smoutput A=:x ,&:< y
 x u y
)

pile_demo =: (,  append Show  locate_for_append)/@:(;/)  NB. pile DATA
unpile_demo =: >@:{[email protected]:((0<#S:0)[email protected]:(transfer Show  smallest)Until(1=#))@:(a:&,)
patience_sort_demo =: [email protected]:pile_demo


import java.util.*;

public class PatienceSort {
    public static <E extends Comparable<? super E>> void sort (E[] n) {
        List<Pile<E>> piles = new ArrayList<Pile<E>>();
        // sort into piles
        for (E x : n) {
            Pile<E> newPile = new Pile<E>();
            newPile.push(x);
            int i = Collections.binarySearch(piles, newPile);
            if (i < 0) i = ~i;
            if (i != piles.size())
                piles.get(i).push(x);
            else
                piles.add(newPile);
        }

        // priority queue allows us to retrieve least pile efficiently
        PriorityQueue<Pile<E>> heap = new PriorityQueue<Pile<E>>(piles);
        for (int c = 0; c < n.length; c++) {
            Pile<E> smallPile = heap.poll();
            n[c] = smallPile.pop();
            if (!smallPile.isEmpty())
                heap.offer(smallPile);
        }
        assert(heap.isEmpty());
    }

    private static class Pile<E extends Comparable<? super E>> extends Stack<E> implements Comparable<Pile<E>> {
        public int compareTo(Pile<E> y) { return peek().compareTo(y.peek()); }
    }

    public static void main(String[] args) {
	Integer[] a = {4, 65, 2, -31, 0, 99, 83, 782, 1};
	sort(a);
	System.out.println(Arrays.toString(a));
    }
}


const patienceSort = (nums) => {
  const piles = []

  for (let i = 0; i < nums.length; i++) {
    const num = nums[i]
    const destinationPileIndex = piles.findIndex(
      (pile) => num >= pile[pile.length - 1]
    )
    if (destinationPileIndex === -1) {
      piles.push([num])
    } else {
      piles[destinationPileIndex].push(num)
    }
  }

  for (let i = 0; i < nums.length; i++) {
    let destinationPileIndex = 0
    for (let p = 1; p < piles.length; p++) {
      const pile = piles[p]
      if (pile[0] < piles[destinationPileIndex][0]) {
        destinationPileIndex = p
      }
    }
    const distPile = piles[destinationPileIndex]
    nums[i] = distPile.shift()
    if (distPile.length === 0) {
      piles.splice(destinationPileIndex, 1)
    }
  }

  return nums
}
console.log(patienceSort([10,6,-30,9,18,1,-20]));


def patienceSort:
  length as $size
  | if $size < 2 then .
    else
      reduce .[] as $e ( {piles: []};
        .outer = false
	| first( range(0; .piles|length) as $ipile
                 | if .piles[$ipile][-1] < $e
                   then .piles[$ipile] += [$e]
                   | .outer = true
		   else empty
		   end ) // .
        | if (.outer|not) then .piles += [[$e]] else . end )
    | reduce range(0; $size) as $i (.;
        .min = .piles[0][0]
        | .minPileIndex = 0
        | reduce range(1; .piles|length) as $j (.;
            if .piles[$j][0] < .min
            then .min = .piles[$j][0]
            | .minPileIndex = $j
	    else .
	    end )
        | .a += [.min]
	| .minPileIndex as $mpx
	| .piles[$mpx] |= .[1:]
        | if (.piles[$mpx] == []) then .piles |= .[:$mpx] + .[$mpx + 1:]
	  else .
	  end)
  end
  | .a ;

[4, 65, 2, -31, 0, 99, 83, 782, 1],
 ["n", "o", "n", "z", "e", "r", "o", "s", "u", "m"],
 ["dog", "cow", "cat", "ape", "ant", "man", "pig", "ass", "gnu"]
| patienceSort


function patiencesort(list::Vector{T}) where T
    piles = Vector{Vector{T}}()
    for n in list
        if isempty(piles) ||
            (i = findfirst(pile -> n <= pile[end], piles)) ==  nothing
            push!(piles, [n])
        else
            push!(piles[i], n)
        end
    end
    mergesorted(piles)
end

function mergesorted(vecvec)
    lengths = map(length, vecvec)
    allsum = sum(lengths)
    sorted = similar(vecvec[1], allsum)
    for i in 1:allsum
        (val, idx) = findmin(map(x -> x[end], vecvec))
        sorted[i] = pop!(vecvec[idx])
        if isempty(vecvec[idx])
            deleteat!(vecvec, idx)
        end
    end
    sorted
end

println(patiencesort(rand(collect(1:1000), 12)))


// version 1.1.2

fun <T : Comparable<T>> patienceSort(arr: Array<T>) {
    if (arr.size < 2) return
    val piles = mutableListOf<MutableList<T>>()
    outer@ for (el in arr) {
        for (pile in piles) {
            if (pile.last() > el) {
                pile.add(el)
                continue@outer
            }
        }
        piles.add(mutableListOf(el))
    }

    for (i in 0 until arr.size) {
        var min = piles[0].last()
        var minPileIndex = 0
        for (j in 1 until piles.size) {
            if (piles[j].last() < min) {
                min = piles[j].last()
                minPileIndex = j
            }
        }
        arr[i] = min
        val minPile = piles[minPileIndex]
        minPile.removeAt(minPile.lastIndex)
        if (minPile.size == 0) piles.removeAt(minPileIndex)
    }
}

fun main(args: Array<String>) {
    val iArr = arrayOf(4, 65, 2, -31, 0, 99, 83, 782, 1)
    patienceSort(iArr)
    println(iArr.contentToString())
    val cArr = arrayOf('n', 'o', 'n', 'z', 'e', 'r', 'o', 's', 'u','m')
    patienceSort(cArr)
    println(cArr.contentToString())
    val sArr = arrayOf("dog", "cow", "cat", "ape", "ant", "man", "pig", "ass", "gnu")
    patienceSort(sArr)
    println(sArr.contentToString())
}


:- module patience_sort_task.

:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.

:- implementation.
:- import_module array.
:- import_module int.
:- import_module list.
:- import_module string.

%%%-------------------------------------------------------------------
%%%
%%% patience_sort/5 -- sorts Array[Ifirst..Ilast] out of place,
%%%                    returning indices in Sorted[0..Ilast-Ifirst].
%%%

:- pred patience_sort(pred(T, T), int, int, array(T), array(int)).
:- mode patience_sort(pred(in, in) is semidet,
                      in, in, in, out) is det.
patience_sort(Less, Ifirst, Ilast, Array, Sorted) :-
  deal(Less, Ifirst, Ilast, Array, Num_piles, Piles, Links),
  k_way_merge(Less, Ifirst, Ilast, Array,
              Num_piles, Piles, Links, Sorted).

%%%-------------------------------------------------------------------
%%%
%%% deal/7 -- deals array elements into piles.
%%%

:- pred deal(pred(T, T), int, int, array(T),
             int, array(int), array(int)).
:- mode deal(pred(in, in) is semidet, in, in, in,
             out, array_uo, array_uo).
deal(Less, Ifirst, Ilast, Array, Num_piles, Piles, Links) :-
  Piles_last = Ilast - Ifirst + 1,
  %% I do not use index zero of arrays, so must allocate one extra
  %% entry per array.
  init(Piles_last + 1, 0, Piles0),
  init(Piles_last + 1, 0, Links0),
  deal_loop(Less, Ifirst, Ilast, Array, 1,
            0, Num_piles,
            Piles0, Piles,
            Links0, Links).

:- pred deal_loop(pred(T, T), int, int, array(T),
                  int, int, int,
                  array(int), array(int),
                  array(int), array(int)).
:- mode deal_loop(pred(in, in) is semidet, in, in, in,
                  in, in, out,
                  array_di, array_uo,
                  array_di, array_uo) is det.
deal_loop(Less, Ifirst, Ilast, Array, Q,
          !Num_piles, !Piles, !Links) :-
  Piles_last = Ilast - Ifirst + 1,
  (if (Q =< Piles_last)
   then (find_pile(Less, Ifirst, Array, !.Num_piles, !.Piles, Q) = I,
         (!.Piles^elem(I)) = L1,
         (!.Piles^elem(I) := Q) = !:Piles,
         (!.Links^elem(Q) := L1) = !:Links,
         max(!.Num_piles, I) = !:Num_piles,
         deal_loop(Less, Ifirst, Ilast, Array, Q + 1,
                   !Num_piles, !Piles, !Links))
   else true).

:- func find_pile(pred(T, T), int, array(T),
                  int, array(int), int) = int.
:- mode find_pile(pred(in, in) is semidet,
                  in, in, in, in, in) = out is det.
find_pile(Less, Ifirst, Array, Num_piles, Piles, Q) = Index :-
  %%
  %% Bottenbruch search for the leftmost pile whose top is greater
  %% than or equal to x. Return an index such that:
  %%
  %%   * if x is greater than the top element at the far right, then
  %%   the index returned will be num-piles.
  %%
  %%   * otherwise, x is greater than every top element to the left of
  %%     index, and less than or equal to the top elements at index
  %%     and to the right of index.
  %%
  %% References:
  %%
  %%   * H. Bottenbruch, "Structure and use of ALGOL 60", Journal of
  %%     the ACM, Volume 9, Issue 2, April 1962, pp.161-221.
  %%     https://doi.org/10.1145/321119.321120
  %%
  %%     The general algorithm is described on pages 214 and 215.
  %%
  %%   * https://en.wikipedia.org/w/index.php?title=Binary_search_algorithm&oldid=1062988272#Alternative_procedure
  %%
  %% Note:
  %%
  %%   * There is a binary search in the array module of the standard
  %%   library, but our search algorithm is known to work in other
  %%   programming languages and is written specifically for the
  %%   situation.
  %%
  (if (Num_piles = 0) then (Index = 1)
   else (find_pile_loop(Less, Ifirst, Array, Piles, Q,
                        0, Num_piles - 1, J),
         (if (J = Num_piles - 1)
          then (I1 = Piles^elem(J + 1) + Ifirst - 1,
                I2 = Q + Ifirst - 1,
                (if Less(Array^elem(I1), Array^elem(I2))
                 then (Index = J + 2)
                 else (Index = J + 1)))
          else (Index = J + 1)))).

:- pred find_pile_loop(pred(T, T), int, array(T), array(int),
                       int, int, int, int).
:- mode find_pile_loop(pred(in, in) is semidet,
                       in, in, in, in, in, in, out) is det.
find_pile_loop(Less, Ifirst, Array, Piles, Q, J, K, J1) :-
  (if (J = K) then (J1 = J)
   else ((J + K) // 2 = I,
         I1 = Piles^elem(J + 1) + Ifirst - 1,
         I2 = Q + Ifirst - 1,
         (if Less(Array^elem(I1), Array^elem(I2))
          then find_pile_loop(Less, Ifirst, Array, Piles, Q,
                              I + 1, K, J1)
          else find_pile_loop(Less, Ifirst, Array, Piles, Q,
                              J, I, J1)))).

%%%-------------------------------------------------------------------
%%%
%%% k_way_merge/8 --
%%%
%%% k-way merge by tournament tree (specific to this patience sort).
%%%
%%% See Knuth, volume 3, and also
%%% https://en.wikipedia.org/w/index.php?title=K-way_merge_algorithm&oldid=1047851465#Tournament_Tree
%%%
%%% However, I store a winners tree instead of the recommended losers
%%% tree. If the tree were stored as linked nodes, it would probably
%%% be more efficient to store a losers tree. However, I am storing
%%% the tree as an array, and one can find an opponent quickly by
%%% simply toggling the least significant bit of a competitor's array
%%% index.
%%%

:- pred k_way_merge(pred(T, T), int, int, array(T), int,
                    array(int), array(int), array(int)).
:- mode k_way_merge(pred(in, in) is semidet,
                    in, in, in, in, array_di, in, out) is det.
%% Contrary to the arrays used internally, the Sorted array is indexed
%% starting at zero.
k_way_merge(Less, Ifirst, Ilast, Array,
            Num_piles, Piles, Links, Sorted) :-
  init(Ilast - Ifirst + 1, 0, Sorted0),
  build_tree(Less, Ifirst, Array, Num_piles, Links, Piles, Piles1,
             Total_external_nodes, Winners_values, Winners_indices),
  k_way_merge_(Less, Ifirst, Array, Piles1, Links,
               Total_external_nodes, Winners_values, Winners_indices,
               0, Sorted0, Sorted).

:- pred k_way_merge_(pred(T, T), int, array(T),
                     array(int), array(int), int,
                     array(int), array(int), int,
                     array(int), array(int)).
:- mode k_way_merge_(pred(in, in) is semidet, in, in, array_di,
                     in, in, array_di, array_di,
                     in, array_di, array_uo) is det.
%% Contrary to the arrays used internally, the Sorted array is indexed
%% starting at zero.
k_way_merge_(Less, Ifirst, Array, Piles, Links, Total_external_nodes,
             Winners_values, Winners_indices, Isorted, !Sorted) :-
  Total_nodes = (2 * Total_external_nodes) - 1,
  (Winners_values^elem(1)) = Value,
  (if (Value = 0) then true
   else (set(Isorted, Value + Ifirst - 1, !Sorted),
         (Winners_indices^elem(1)) = Index,
         (Piles^elem(Index)) = Next, % The next top of pile Index.
         (if (Next \= 0)        % Drop that top of pile.
          then (Links^elem(Next) = Link,
                set(Index, Link, Piles, Piles1))
          else (Piles = Piles1)),
         (Total_nodes // 2) + Index = I,
         (Winners_values^elem(I) := Next) = Winners_values1,
         replay_games(Less, Ifirst, Array, I,
                      Winners_values1, Winners_values2,
                      Winners_indices, Winners_indices1),
         k_way_merge_(Less, Ifirst, Array, Piles1, Links,
                      Total_external_nodes, Winners_values2,
                      Winners_indices1, Isorted + 1, !Sorted))).

:- pred build_tree(pred(T, T), int, array(T), int, array(int),
                   array(int), array(int), int, array(int),
                   array(int)).
:- mode build_tree(pred(in, in) is semidet, in, in, in, in,
                   array_di, array_uo, out, out, out) is det.
build_tree(Less, Ifirst, Array, Num_piles, Links, !Piles,
           Total_external_nodes, Winners_values, Winners_indices) :-
  Total_external_nodes = next_power_of_two(Num_piles),
  Total_nodes = (2 * Total_external_nodes) - 1,
  %% I do not use index zero of arrays, so must allocate one extra
  %% entry per array.
  init(Total_nodes + 1, 0, Winners_values0),
  init(Total_nodes + 1, 0, Winners_indices0),
  init_winners_pile_indices(Total_external_nodes, 1,
                            Winners_indices0, Winners_indices1),
  init_starting_competitors(Total_external_nodes, Num_piles,
                            (!.Piles), 1, Winners_values0,
                            Winners_values1),
  discard_initial_tops_of_piles(Num_piles, Links, 1, !Piles),
  play_initial_games(Less, Ifirst, Array,
                     Total_external_nodes,
                     Winners_values1, Winners_values,
                     Winners_indices1, Winners_indices).

:- pred init_winners_pile_indices(int::in, int::in,
                                  array(int)::array_di,
                                  array(int)::array_uo) is det.
init_winners_pile_indices(Total_external_nodes, I,
                          !Winners_indices) :-
  (if (I = Total_external_nodes + 1) then true
   else (set(Total_external_nodes - 1 + I, I, !Winners_indices),
         init_winners_pile_indices(Total_external_nodes, I + 1,
                                   !Winners_indices))).

:- pred init_starting_competitors(int::in, int::in,
                                  array(int)::in, int::in,
                                  array(int)::array_di,
                                  array(int)::array_uo) is det.
init_starting_competitors(Total_external_nodes, Num_piles,
                          Piles, I, !Winners_values) :-
  (if (I = Num_piles + 1) then true
   else (Piles^elem(I) = Value,
         set(Total_external_nodes - 1 + I, Value, !Winners_values),
         init_starting_competitors(Total_external_nodes, Num_piles,
                                   Piles, I + 1, !Winners_values))).

:- pred discard_initial_tops_of_piles(int::in, array(int)::in,
                                      int::in, array(int)::array_di,
                                      array(int)::array_uo) is det.
discard_initial_tops_of_piles(Num_piles, Links, I, !Piles) :-
  (if (I = Num_piles + 1) then true
   else ((!.Piles^elem(I)) = Old_value,
         Links^elem(Old_value) = New_value,
         set(I, New_value, !Piles),
         discard_initial_tops_of_piles(Num_piles, Links, I + 1,
                                       !Piles))).

:- pred play_initial_games(pred(T, T), int, array(T), int,
                           array(int), array(int),
                           array(int), array(int)).
:- mode play_initial_games(pred(in, in) is semidet,
                           in, in, in,
                           array_di, array_uo,
                           array_di, array_uo) is det.
play_initial_games(Less, Ifirst, Array, Istart,
                   !Winners_values, !Winners_indices) :-
  (if (Istart = 1) then true
   else (play_an_initial_round(Less, Ifirst, Array, Istart, Istart,
                               !Winners_values, !Winners_indices),
         play_initial_games(Less, Ifirst, Array, Istart // 2,
                            !Winners_values, !Winners_indices))).

:- pred play_an_initial_round(pred(T, T), int, array(T), int, int,
                              array(int), array(int),
                              array(int), array(int)).
:- mode play_an_initial_round(pred(in, in) is semidet,
                              in, in, in, in,
                              array_di, array_uo,
                              array_di, array_uo) is det.
play_an_initial_round(Less, Ifirst, Array, Istart, I,
                      !Winners_values, !Winners_indices) :-
  (if ((2 * Istart) - 1 < I) then true
   else (play_game(Less, Ifirst, Array,
                   !.Winners_values, I) = Iwinner,
         (!.Winners_values^elem(Iwinner)) = Value,
         (!.Winners_indices^elem(Iwinner)) = Index,
         I // 2 = Iparent,
         set(Iparent, Value, !Winners_values),
         set(Iparent, Index, !Winners_indices),
         play_an_initial_round(Less, Ifirst, Array, Istart, I + 2,
                               !Winners_values, !Winners_indices))).

:- pred replay_games(pred(T, T), int, array(T), int,
                     array(int), array(int),
                     array(int), array(int)).
:- mode replay_games(pred(in, in) is semidet, in, in, in,
                     array_di, array_uo,
                     array_di, array_uo) is det.
replay_games(Less, Ifirst, Array, I,
             !Winners_values, !Winners_indices) :-
  (if (I = 1) then true
   else (Iwinner = play_game(Less, Ifirst, Array,
                             !.Winners_values, I),
         (!.Winners_values^elem(Iwinner)) = Value,
         (!.Winners_indices^elem(Iwinner)) = Index,
         I // 2 = Iparent,
         set(Iparent, Value, !Winners_values),
         set(Iparent, Index, !Winners_indices),
         replay_games(Less, Ifirst, Array, Iparent,
                      !Winners_values, !Winners_indices))).

:- func play_game(pred(T, T), int, array(T), array(int), int) = int.
:- mode play_game(pred(in, in) is semidet,
                  in, in, in, in) = out is det.
play_game(Less, Ifirst, Array, Winners_values, I) = Iwinner :-
  J = xor(I, 1),                % Find an opponent.
  Winners_values^elem(I) = Value_I,
  (if (Value_I = 0) then (Iwinner = J)
   else (Winners_values^elem(J) = Value_J,
         (if (Value_J = 0) then (Iwinner = I)
          else (AJ = Array^elem(Value_J + Ifirst - 1),
                AI = Array^elem(Value_I + Ifirst - 1),
                (if Less(AJ, AI) then (Iwinner = J)
                 else (Iwinner = I)))))).

%%%-------------------------------------------------------------------

:- func next_power_of_two(int) = int.
%% This need not be a fast implemention.
next_power_of_two(N) = next_power_of_two_(N, 1).

:- func next_power_of_two_(int, int) = int.
next_power_of_two_(N, I) = Pow2 :-
  if (I < N) then (Pow2 = next_power_of_two_(N, I + I))
  else (Pow2 = I).

%%%-------------------------------------------------------------------

:- func example_numbers = list(int).
example_numbers = [22, 15, 98, 82, 22, 4, 58, 70, 80, 38, 49, 48, 46,
                   54, 93, 8, 54, 2, 72, 84, 86, 76, 53, 37, 90].

main(!IO) :-
  from_list(example_numbers, Array),
  bounds(Array, Ifirst, Ilast),
  patience_sort(<, Ifirst, Ilast, Array, Sorted),
  print("unsorted  ", !IO),
  print_int_array(Array, Ifirst, !IO),
  print_line("", !IO),
  print("sorted    ", !IO),
  print_indirect_array(Sorted, Array, 0, !IO),
  print_line("", !IO).

:- pred print_int_array(array(int)::in, int::in,
                        io::di, io::uo) is det.
print_int_array(Array, I, !IO) :-
  bounds(Array, _, Ilast),
  (if (I = Ilast + 1) then true
   else (print(" ", !IO),
         print(from_int(Array^elem(I)), !IO),
         print_int_array(Array, I + 1, !IO))).

:- pred print_indirect_array(array(int)::in, array(int)::in,
                             int::in, io::di, io::uo) is det.
print_indirect_array(Sorted, Array, I, !IO) :-
  bounds(Sorted, _, Ilast),
  (if (I = Ilast + 1) then true
   else (print(" ", !IO),
         print(from_int(Array^elem(Sorted^elem(I))), !IO),
         print_indirect_array(Sorted, Array, I + 1, !IO))).

%%%-------------------------------------------------------------------
%%% local variables:
%%% mode: mercury
%%% prolog-indent-width: 2
%%% end:


MODULE PatienceSortTask;

FROM STextIO IMPORT WriteString;
FROM STextIO IMPORT WriteLn;
FROM WholeStr IMPORT IntToStr;

CONST MaxSortSize = 1024;       (* A power of two. *)
      MaxWinnersSize = (2 * MaxSortSize) - 1;

TYPE PilesArrayType = ARRAY [1 .. MaxSortSize] OF INTEGER;
     WinnersArrayType = ARRAY [1 .. MaxWinnersSize],
                              [1 .. 2] OF INTEGER;

VAR ExampleNumbers : ARRAY [0 .. 35] OF INTEGER;
    SortedIndices : ARRAY [0 .. 25] OF INTEGER;
    i : INTEGER;
    NumStr : ARRAY [0 .. 2] OF CHAR;

PROCEDURE NextPowerOfTwo (n : INTEGER) : INTEGER;
  VAR Pow2 : INTEGER;
BEGIN
  (* This need not be a fast implementation. *)
  Pow2 := 1;
  WHILE Pow2 < n DO
    Pow2 := Pow2 + Pow2;
  END;
  RETURN Pow2;
END NextPowerOfTwo;

PROCEDURE InitPilesArray (VAR Arr : PilesArrayType);
  VAR i : INTEGER;
BEGIN
  FOR i := 1 TO MaxSortSize DO
    Arr[i] := 0;
  END;
END InitPilesArray;

PROCEDURE InitWinnersArray (VAR Arr : WinnersArrayType);
  VAR i : INTEGER;
BEGIN
  FOR i := 1 TO MaxWinnersSize DO
    Arr[i, 1] := 0;
    Arr[i, 2] := 0;
  END;
END InitWinnersArray;

PROCEDURE IntegerPatienceSort (iFirst, iLast : INTEGER;
                               Arr : ARRAY OF INTEGER;
                               VAR Sorted : ARRAY OF INTEGER);
  VAR NumPiles : INTEGER;
      Piles, Links : PilesArrayType;
      Winners : WinnersArrayType;

  PROCEDURE FindPile (q : INTEGER) : INTEGER;
    (*
       Bottenbruch search for the leftmost pile whose top is greater
       than or equal to some element x. Return an index such that:

       * if x is greater than the top element at the far right, then
         the index returned will be num-piles.

       * otherwise, x is greater than every top element to the left of
         index, and less than or equal to the top elements at index
         and to the right of index.

       References:

       * H. Bottenbruch, "Structure and use of ALGOL 60", Journal of
         the ACM, Volume 9, Issue 2, April 1962, pp.161-221.
         https://doi.org/10.1145/321119.321120

         The general algorithm is described on pages 214 and 215.

       * https://en.wikipedia.org/w/index.php?title=Binary_search_algorithm&oldid=1062988272#Alternative_procedure
     *)
    VAR i, j, k, Index : INTEGER;
  BEGIN
    IF NumPiles = 0 THEN
      Index := 1;
    ELSE
      j := 0;
      k := NumPiles - 1;
      WHILE j <> k DO
        i := (j + k) DIV 2;
        IF Arr[Piles[j + 1] + iFirst - 1] < Arr[q + iFirst - 1] THEN
          j := i + 1;
        ELSE
          k := i;
        END;
      END;
      IF j = NumPiles - 1 THEN
        IF Arr[Piles[j + 1] + iFirst - 1] < Arr[q + iFirst - 1] THEN
          (* A new pile is needed. *)
          j := j + 1;
        END;
      END;
      Index := j + 1;
    END;
    RETURN Index;
  END FindPile;

  PROCEDURE Deal;
    VAR i, q : INTEGER;
  BEGIN
    FOR q := 1 TO iLast - iFirst + 1 DO
      i := FindPile (q);
      Links[q] := Piles[i];
      Piles[i] := q;
      IF i = NumPiles + 1 THEN
        NumPiles := i;
      END;
    END;
  END Deal;

  PROCEDURE KWayMerge;
    (*
       k-way merge by tournament tree.

       See Knuth, volume 3, and also
       https://en.wikipedia.org/w/index.php?title=K-way_merge_algorithm&oldid=1047851465#Tournament_Tree

       However, I store a winners tree instead of the recommended
       losers tree. If the tree were stored as linked nodes, it would
       probably be more efficient to store a losers tree. However, I
       am storing the tree as an array, and one can find an opponent
       quickly by simply toggling the least significant bit of a
       competitor's array index.
     *)
    VAR TotalExternalNodes : INTEGER;
        TotalNodes : INTEGER;
        iSorted, i, Next : INTEGER;

    PROCEDURE FindOpponent (i : INTEGER) : INTEGER;
      VAR Opponent : INTEGER;
    BEGIN
      IF ODD (i) THEN
        Opponent := i - 1;
      ELSE
        Opponent := i + 1;
      END;
      RETURN Opponent;
    END FindOpponent;

    PROCEDURE PlayGame (i : INTEGER) : INTEGER;
      VAR j, iWinner : INTEGER;
    BEGIN
      j := FindOpponent (i);
      IF Winners[i, 1] = 0 THEN
        iWinner := j;
      ELSIF Winners[j, 1] = 0 THEN
        iWinner := i;
      ELSIF Arr[Winners[j, 1] + iFirst - 1]
               < Arr[Winners[i, 1] + iFirst - 1] THEN
        iWinner := j;
      ELSE
        iWinner := i;
      END;
      RETURN iWinner;
    END PlayGame;

    PROCEDURE ReplayGames (i : INTEGER);
      VAR j, iWinner : INTEGER;
    BEGIN
      j := i;
      WHILE j <> 1 DO
        iWinner := PlayGame (j);
        j := j DIV 2;
        Winners[j, 1] := Winners[iWinner, 1];
        Winners[j, 2] := Winners[iWinner, 2];
      END;
    END ReplayGames;

    PROCEDURE BuildTree;
      VAR iStart, i, iWinner : INTEGER;
    BEGIN
      FOR i := 1 TO TotalExternalNodes DO
        (* Record which pile a winner will have come from. *)
        Winners[TotalExternalNodes - 1 + i, 2] := i;
      END;

      FOR i := 1 TO NumPiles DO
        (* The top of each pile becomes a starting competitor. *)
        Winners[TotalExternalNodes + i - 1, 1] := Piles[i];
      END;

      FOR i := 1 TO NumPiles DO
        (* Discard the top of each pile. *)
        Piles[i] := Links[Piles[i]];
      END;

      iStart := TotalExternalNodes;
      WHILE iStart <> 1 DO
        FOR i := iStart TO (2 * iStart) - 1 BY 2 DO
          iWinner := PlayGame (i);
          Winners[i DIV 2, 1] := Winners[iWinner, 1];
          Winners[i DIV 2, 2] := Winners[iWinner, 2];
        END;
        iStart := iStart DIV 2;
      END;
    END BuildTree;

  BEGIN
    TotalExternalNodes := NextPowerOfTwo (NumPiles);
    TotalNodes := (2 * TotalExternalNodes) - 1;
    BuildTree;
    iSorted := 0;
    WHILE Winners[1, 1] <> 0 DO
      Sorted[iSorted] := Winners[1, 1] + iFirst - 1;
      iSorted := iSorted + 1;
      i := Winners[1, 2];
      Next := Piles[i];         (* The next top of pile i. *)
      IF Next <> 0 THEN
        Piles[i] := Links[Next]; (* Drop that top. *)
      END;
      i := (TotalNodes DIV 2) + i;
      Winners[i, 1] := Next;
      ReplayGames (i);
    END;
  END KWayMerge;

BEGIN
  NumPiles := 0;
  InitPilesArray (Piles);
  InitPilesArray (Links);
  InitWinnersArray (Winners);

  IF MaxSortSize < iLast - iFirst + 1 THEN
    WriteString ('This subarray is too large for the program.');
    WriteLn;
    HALT;
  ELSE
    Deal;
    KWayMerge;
  END;
END IntegerPatienceSort;

BEGIN
  ExampleNumbers[10] := 22;
  ExampleNumbers[11] := 15;
  ExampleNumbers[12] := 98;
  ExampleNumbers[13] := 82;
  ExampleNumbers[14] := 22;
  ExampleNumbers[15] := 4;
  ExampleNumbers[16] := 58;
  ExampleNumbers[17] := 70;
  ExampleNumbers[18] := 80;
  ExampleNumbers[19] := 38;
  ExampleNumbers[20] := 49;
  ExampleNumbers[21] := 48;
  ExampleNumbers[22] := 46;
  ExampleNumbers[23] := 54;
  ExampleNumbers[24] := 93;
  ExampleNumbers[25] := 8;
  ExampleNumbers[26] := 54;
  ExampleNumbers[27] := 2;
  ExampleNumbers[28] := 72;
  ExampleNumbers[29] := 84;
  ExampleNumbers[30] := 86;
  ExampleNumbers[31] := 76;
  ExampleNumbers[32] := 53;
  ExampleNumbers[33] := 37;
  ExampleNumbers[34] := 90;

  IntegerPatienceSort (10, 34, ExampleNumbers, SortedIndices);

  WriteString ("unsorted  ");
  FOR i := 10 TO 34 DO
    WriteString (" ");
    IntToStr (ExampleNumbers[i], NumStr);
    WriteString (NumStr);
  END;
  WriteLn;
  WriteString ("sorted    ");
  FOR i := 0 TO 24 DO
    WriteString (" ");
    IntToStr (ExampleNumbers[SortedIndices[i]], NumStr);
    WriteString (NumStr);
  END;
  WriteLn;
END PatienceSortTask.


import std/decls

func patienceSort[T](a: var openArray[T]) =

  if a.len < 2: return

  var piles: seq[seq[T]]

  for elem in a:
    block processElem:
      for pile in piles.mitems:
        if pile[^1] > elem:
          pile.add(elem)
          break processElem
      piles.add(@[elem])

  for i in 0..a.high:
    var min = piles[0][^1]
    var minPileIndex = 0
    for j in 1..piles.high:
      if piles[j][^1] < min:
        min = piles[j][^1]
        minPileIndex = j

    a[i] = min
    var minPile {.byAddr.} = piles[minPileIndex]
    minPile.setLen(minpile.len - 1)
    if minPile.len == 0: piles.delete(minPileIndex)

when isMainModule:

  var iArray = [4, 65, 2, -31, 0, 99, 83, 782, 1]
  iArray.patienceSort()
  echo iArray
  var cArray = ['n', 'o', 'n', 'z', 'e', 'r', 'o', 's', 'u','m']
  cArray.patienceSort()
  echo cArray
  var sArray = ["dog", "cow", "cat", "ape", "ant", "man", "pig", "ass", "gnu"]
  sArray.patienceSort()
  echo sArray


module PatienceSortFn (Ord : Set.OrderedType) : sig
    val patience_sort : Ord.t list -> Ord.t list
  end = struct

  module PilesSet = Set.Make
    (struct
       type t = Ord.t list
       let compare x y = Ord.compare (List.hd x) (List.hd y)
     end);;

  let sort_into_piles list =
    let piles = Array.make (List.length list) [] in
    let bsearch_piles x len =
      let rec aux lo hi =
        if lo > hi then
          lo
        else
          let mid = (lo + hi) / 2 in
          if Ord.compare (List.hd piles.(mid)) x < 0 then
            aux (mid+1) hi
          else
            aux lo (mid-1)
      in
        aux 0 (len-1)
    in
    let f len x =
      let i = bsearch_piles x len in
      piles.(i) <- x :: piles.(i);
      if i = len then len+1 else len
    in
    let len = List.fold_left f 0 list in
    Array.sub piles 0 len

  let merge_piles piles =
    let pq = Array.fold_right PilesSet.add piles PilesSet.empty in
    let rec f pq acc =
      if PilesSet.is_empty pq then
        acc
      else
        let elt = PilesSet.min_elt pq in
        match elt with
          [] -> failwith "Impossible"
        | x::xs ->
          let pq' = PilesSet.remove elt pq in
          f (if xs = [] then pq' else PilesSet.add xs pq') (x::acc)
    in
    List.rev (f pq [])

  let patience_sort n =
    merge_piles (sort_into_piles n)
end


PatienceSortTask (Output);

CONST MaxSortSize = 1024;       { A power of two. }
      MaxWinnersSize = (2 * MaxSortSize) - 1;

TYPE PilesArrayType = ARRAY [1 .. MaxSortSize] OF INTEGER;
     WinnersArrayType = ARRAY [1 .. MaxWinnersSize,
                               1 .. 2] OF INTEGER;

VAR ExampleNumbers : ARRAY [0 .. 35] OF INTEGER;
    SortedIndices : ARRAY [0 .. 25] OF INTEGER;
    i : INTEGER;

FUNCTION NextPowerOfTwo (n : INTEGER) : INTEGER;
  VAR Pow2 : INTEGER;
BEGIN
  { This need not be a fast implementation. }
  Pow2 := 1;
  WHILE Pow2 < n DO
    Pow2 := Pow2 + Pow2;
  NextPowerOfTwo := Pow2;
END;

PROCEDURE InitPilesArray (VAR Arr : PilesArrayType);
  VAR i : INTEGER;
BEGIN
  FOR i := 1 TO MaxSortSize DO
    Arr[i] := 0;
END;

PROCEDURE InitWinnersArray (VAR Arr : WinnersArrayType);
  VAR i : INTEGER;
BEGIN
  FOR i := 1 TO MaxWinnersSize DO
    BEGIN
      Arr[i, 1] := 0;
      Arr[i, 2] := 0;
    END;
END;

PROCEDURE IntegerPatienceSort (iFirst, iLast : INTEGER;
                               Arr : ARRAY OF INTEGER;
                               VAR Sorted : ARRAY OF INTEGER);
  VAR NumPiles : INTEGER;
      Piles, Links : PilesArrayType;
      Winners : WinnersArrayType;

  FUNCTION FindPile (q : INTEGER) : INTEGER;
    {
       Bottenbruch search for the leftmost pile whose top is greater
       than or equal to some element x. Return an index such that:

       * if x is greater than the top element at the far right, then
         the index returned will be num-piles.

       * otherwise, x is greater than every top element to the left of
         index, and less than or equal to the top elements at index
         and to the right of index.

       References:

       * H. Bottenbruch, "Structure and use of ALGOL 60", Journal of
         the ACM, Volume 9, Issue 2, April 1962, pp.161-221.
         https://doi.org/10.1145/321119.321120

         The general algorithm is described on pages 214 and 215.

       * https://en.wikipedia.org/w/index.php?title=Binary_search_algorithm&oldid=1062988272#Alternative_procedure
    }
    VAR i, j, k, Index : INTEGER;
  BEGIN
    IF NumPiles = 0 THEN
      Index := 1
    ELSE
      BEGIN
        j := 0;
        k := NumPiles - 1;
        WHILE j <> k DO
          BEGIN
            i := (j + k) DIV 2;
            IF Arr[Piles[j + 1] + iFirst - 1] < Arr[q + iFirst - 1] THEN
              j := i + 1
            ELSE
              k := i
          END;
        IF j = NumPiles - 1 THEN
          BEGIN
            IF Arr[Piles[j + 1] + iFirst - 1] < Arr[q + iFirst - 1] THEN
              { A new pile is needed. }
              j := j + 1
          END;
        Index := j + 1
      END;
    FindPile := Index
  END;

  PROCEDURE Deal;
    VAR i, q : INTEGER;
  BEGIN
    FOR q := 1 TO iLast - iFirst + 1 DO
      BEGIN
        i := FindPile (q);
        Links[q] := Piles[i];
        Piles[i] := q;
        IF i = NumPiles + 1 THEN
          NumPiles := i
      END
  END;

  PROCEDURE KWayMerge;
    {
       k-way merge by tournament tree.

       See Knuth, volume 3, and also
       https://en.wikipedia.org/w/index.php?title=K-way_merge_algorithm&oldid=1047851465#Tournament_Tree

       However, I store a winners tree instead of the recommended
       losers tree. If the tree were stored as linked nodes, it would
       probably be more efficient to store a losers tree. However, I
       am storing the tree as an array, and one can find an opponent
       quickly by simply toggling the least significant bit of a
       competitor's array index.
    }
    VAR TotalExternalNodes : INTEGER;
        TotalNodes : INTEGER;
        iSorted, i, Next : INTEGER;

    FUNCTION FindOpponent (i : INTEGER) : INTEGER;
      VAR Opponent : INTEGER;
    BEGIN
      IF ODD (i) THEN
        Opponent := i - 1
      ELSE
        Opponent := i + 1;
      FindOpponent := Opponent
    END;

    FUNCTION PlayGame (i : INTEGER) : INTEGER;
      VAR j, iWinner : INTEGER;
    BEGIN
      j := FindOpponent (i);
      IF Winners[i, 1] = 0 THEN
        iWinner := j
      ELSE IF Winners[j, 1] = 0 THEN
        iWinner := i
      ELSE IF (Arr[Winners[j, 1] + iFirst - 1]
               < Arr[Winners[i, 1] + iFirst - 1]) THEN
        iWinner := j
      ELSE
        iWinner := i;
      PlayGame := iWinner
    END;

    PROCEDURE ReplayGames (i : INTEGER);
      VAR j, iWinner : INTEGER;
    BEGIN
      j := i;
      WHILE j <> 1 DO
        BEGIN
          iWinner := PlayGame (j);
          j := j DIV 2;
          Winners[j, 1] := Winners[iWinner, 1];
          Winners[j, 2] := Winners[iWinner, 2];
        END
    END;

    PROCEDURE BuildTree;
      VAR iStart, i, iWinner : INTEGER;
    BEGIN
      FOR i := 1 TO TotalExternalNodes DO
        { Record which pile a winner will have come from. }
        Winners[TotalExternalNodes - 1 + i, 2] := i;

      FOR i := 1 TO NumPiles DO
        { The top of each pile becomes a starting competitor. }
        Winners[TotalExternalNodes + i - 1, 1] := Piles[i];

      FOR i := 1 TO NumPiles DO
        { Discard the top of each pile. }
        Piles[i] := Links[Piles[i]];

      iStart := TotalExternalNodes;
      WHILE iStart <> 1 DO
        BEGIN
          i := iStart;
          WHILE i <= (2 * iStart) - 1 DO
            BEGIN
              iWinner := PlayGame (i);
              Winners[i DIV 2, 1] := Winners[iWinner, 1];
              Winners[i DIV 2, 2] := Winners[iWinner, 2];
              i := i + 2
            END;
          iStart := iStart DIV 2
        END
    END;

  BEGIN
    TotalExternalNodes := NextPowerOfTwo (NumPiles);
    TotalNodes := (2 * TotalExternalNodes) - 1;
    BuildTree;
    iSorted := 0;
    WHILE Winners[1, 1] <> 0 DO
      BEGIN
        Sorted[iSorted] := Winners[1, 1] + iFirst - 1;
        iSorted := iSorted + 1;
        i := Winners[1, 2];
        Next := Piles[i];         { The next top of pile i. }
        IF Next <> 0 THEN
          Piles[i] := Links[Next]; { Drop that top. }
        i := (TotalNodes DIV 2) + i;
        Winners[i, 1] := Next;
        ReplayGames (i)
      END
  END;

BEGIN
  NumPiles := 0;
  InitPilesArray (Piles);
  InitPilesArray (Links);
  InitWinnersArray (Winners);

  IF MaxSortSize < iLast - iFirst + 1 THEN
    BEGIN
      Write ('This subarray is too large for the program.');
      WriteLn;
      HALT
    END
  ELSE
    BEGIN
      Deal;
      KWayMerge
    END
END;

BEGIN
  ExampleNumbers[10] := 22;
  ExampleNumbers[11] := 15;
  ExampleNumbers[12] := 98;
  ExampleNumbers[13] := 82;
  ExampleNumbers[14] := 22;
  ExampleNumbers[15] := 4;
  ExampleNumbers[16] := 58;
  ExampleNumbers[17] := 70;
  ExampleNumbers[18] := 80;
  ExampleNumbers[19] := 38;
  ExampleNumbers[20] := 49;
  ExampleNumbers[21] := 48;
  ExampleNumbers[22] := 46;
  ExampleNumbers[23] := 54;
  ExampleNumbers[24] := 93;
  ExampleNumbers[25] := 8;
  ExampleNumbers[26] := 54;
  ExampleNumbers[27] := 2;
  ExampleNumbers[28] := 72;
  ExampleNumbers[29] := 84;
  ExampleNumbers[30] := 86;
  ExampleNumbers[31] := 76;
  ExampleNumbers[32] := 53;
  ExampleNumbers[33] := 37;
  ExampleNumbers[34] := 90;

  IntegerPatienceSort (10, 34, ExampleNumbers, SortedIndices);

  Write ('unsorted  ');
  FOR i := 10 TO 34 DO
    BEGIN
      Write (' ');
      Write (ExampleNumbers[i])
    END;
  WriteLn;
  Write ('sorted    ');
  FOR i := 0 TO 24 DO
    BEGIN
      Write (' ');
      Write (ExampleNumbers[SortedIndices[i]]);
    END;
  WriteLn
END.


sub patience_sort {
    my @s = [shift];
    for my $card (@_) {
	my @t = grep { $_->[-1] > $card } @s;
	if (@t) { push @{shift(@t)}, $card }
	else { push @s, [$card] }
    }
    my @u;
    while (my @v = grep @$_, @s) {
	my $value = (my $min = shift @v)->[-1];
	for (@v) {
	    ($min, $value) =
	    ($_, $_->[-1]) if $_->[-1] < $value
	}
	push @u, pop @$min;
    }
    return @u
}

print join ' ', patience_sort qw(4 3 6 2 -1 13 12 9);


with javascript_semantics

function patience_sort(sequence s)
    -- create list of sorted lists
    sequence piles = {}
    for i=1 to length(s) do
        object n = s[i]
        for p=1 to length(piles)+1 do
            if p>length(piles) then
                piles = append(piles,{n})
            elsif n>=piles[p][$] then
                piles[p] = append(deep_copy(piles[p]),n)
                exit
            end if
        end for
    end for
    -- merge sort the piles
    sequence res = ""
    while length(piles) do
        integer idx = smallest(piles,return_index:=true)
        res = append(res,piles[idx][1])
        if length(piles[idx])=1 then
            piles[idx..idx] = {}
        else
            piles[idx] = piles[idx][2..$]
        end if
    end while
    return res
end function

constant tests = {{4,65,2,-31,0,99,83,782,1},
                  {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15},
                  "nonzerosum",
                  {"dog", "cow", "cat", "ape", "ant", "man", "pig", "ass", "gnu"}}

for i=1 to length(tests) do
    pp(patience_sort(tests[i]),{pp_IntCh,false})
end for


<?php
class PilesHeap extends SplMinHeap {
    public function compare($pile1, $pile2) {
        return parent::compare($pile1->top(), $pile2->top());
    }
}

function patience_sort(&$n) {
    $piles = array();
    // sort into piles
    foreach ($n as $x) {
        // binary search
        $low = 0; $high = count($piles)-1;
        while ($low <= $high) {
            $mid = (int)(($low + $high) / 2);
            if ($piles[$mid]->top() >= $x)
                $high = $mid - 1;
            else
                $low = $mid + 1;
        }
        $i = $low;
        if ($i == count($piles))
            $piles[] = new SplStack();
        $piles[$i]->push($x);
    }

    // priority queue allows us to merge piles efficiently
    $heap = new PilesHeap();
    foreach ($piles as $pile)
        $heap->insert($pile);
    for ($c = 0; $c < count($n); $c++) {
        $smallPile = $heap->extract();
        $n[$c] = $smallPile->pop();
        if (!$smallPile->isEmpty())
        $heap->insert($smallPile);
    }
    assert($heap->isEmpty());
}

$a = array(4, 65, 2, -31, 0, 99, 83, 782, 1);
patience_sort($a);
print_r($a);
?>


(de leftmost (Lst N H)
   (let L 1
      (while (<= L H)
         (use (X)
            (setq X (/ (+ L H) 2))
         (if (>= (caar (nth Lst X)) N)
               (setq H (dec X))
               (setq L (inc X)) ) ) )
      L ) )

(de patience (Lst)
   (let (L (cons (cons (car Lst)))  C 1  M NIL)
      (for N (cdr Lst)
         (let I (leftmost L N C)
            (and
               (> I C)
               (conc L (cons NIL))
               (inc 'C) )
            (push (nth L I) N) ) )
      (make
         (loop
            (setq M (cons 0 T))
            (for (I . Y) L
               (let? S (car Y)
                  (and
                     (< S (cdr M))
                     (setq M (cons I S)) ) ) )
            (T (=T (cdr M)))
            (link (pop (nth L (car M)))) ) ) ) )

(println
   (patience (4 65 2 -31 0 99 83 782 1)) )

(bye)


patience_sort(UnSorted,Sorted) :-
	make_piles(UnSorted,[],Piled),
	merge_piles(Piled,[],Sorted).

make_piles([],P,P).
make_piles([N|T],[],R) :-
	make_piles(T,[[N]],R).
make_piles([N|T],[[P|Pnt]|Tp],R) :-
	N =< P,
	make_piles(T,[[N,P|Pnt]|Tp],R).
make_piles([N|T],[[P|Pnt]|Tp],R) :-
	N > P,
	make_piles(T,[[N],[P|Pnt]|Tp], R).

merge_piles([],M,M).
merge_piles([P|T],L,R) :-
	merge_pile(P,L,Pl),
	merge_piles(T,Pl,R).

merge_pile([],M,M).
merge_pile(M,[],M).
merge_pile([N|T1],[N|T2],[N,N|R]) :-
	merge_pile(T1,T2,R).
merge_pile([N|T1],[P|T2],[P|R]) :-
	N > P,
	merge_pile([N|T1],T2,R).
merge_pile([N|T1],[P|T2],[N|R]) :-
	N < P,
	merge_pile(T1,[P|T2],R).


from functools import total_ordering
from bisect import bisect_left
from heapq import merge

@total_ordering
class Pile(list):
    def __lt__(self, other): return self[-1] < other[-1]
    def __eq__(self, other): return self[-1] == other[-1]

def patience_sort(n):
    piles = []
    # sort into piles
    for x in n:
        new_pile = Pile([x])
        i = bisect_left(piles, new_pile)
        if i != len(piles):
            piles[i].append(x)
        else:
            piles.append(new_pile)

    # use a heap-based merge to merge piles efficiently
    n[:] = merge(*[reversed(pile) for pile in piles])

if __name__ == "__main__":
    a = [4, 65, 2, -31, 0, 99, 83, 782, 1]
    patience_sort(a)
    print a


  [ dip [ 0 over size rot ]
    nested bsearchwith
      [ -1 peek
        dip [ -1 peek ] > ]
    drop ]                       is searchpiles ( [ n --> n )

  [ dup size dup 1 = iff
    [ drop 0 peek ] done
    2 / split
    recurse swap recurse
    merge ]                      is k-merge     (   [ --> [ )

  [ 1 split dip nested
    witheach
      [ 2dup dip dup
        searchpiles
        over size over = iff
          [ 2drop
            nested nested join ]
        else
          [ dup dip
              [ peek swap join
                swap ]
            poke ] ]
    k-merge ]                    is patience-sort ( [ --> [ )

  ' [ 0 1 2 3 4 5 6 7 8 9 ]
  shuffle dup echo cr
  patience-sort echo