BEGIN

    ! ABSTRACTION FOR SIMULA'S TWO NUMERIC TYPES ;
    CLASS NUMBER;
    VIRTUAL:
        PROCEDURE OUT IS PROCEDURE OUT;;
    BEGIN
    END NUMBER;

    NUMBER CLASS INTEGERNUMBER(INTVAL); INTEGER INTVAL;
    BEGIN
        PROCEDURE OUT; OUTINT(INTVAL, 10);
    END INTEGERNUMBER;

    NUMBER CLASS REALNUMBER(REALVAL); REAL REALVAL;
    BEGIN
        PROCEDURE OUT; OUTFIX(REALVAL, 4, 10);
    END REALNUMBER;

    ! SIMULA CANNOT RETURN FUNCTIONS - SIMULATE FUNCTIONS WITH CLASSES ;
    CLASS ACCUMULATOR(ACC); REF(NUMBER) ACC;
    BEGIN

        PROCEDURE SWITCHTOREAL(Y); REAL Y;
        BEGIN
            REF(REALNUMBER) NEWACC;
            NEWACC :- NEW REALNUMBER(ACC QUA INTEGERNUMBER.INTVAL);
            NEWACC.REALVAL:= NEWACC.REALVAL + Y;
            ACC :- NEWACC;
        END SWITCHTOREAL;

        REF(NUMBER) PROCEDURE ACCUMULATE(OTHERNUM); REF(NUMBER) OTHERNUM;
        BEGIN
            INSPECT ACC
            WHEN INTEGERNUMBER DO
                BEGIN
                    INSPECT OTHERNUM
                    WHEN INTEGERNUMBER DO
                        ACC QUA INTEGERNUMBER.INTVAL
                            := ACC QUA INTEGERNUMBER.INTVAL + INTVAL
                    WHEN REALNUMBER DO
                        SWITCHTOREAL(REALVAL)
                END
            WHEN REALNUMBER DO
                BEGIN
                    INSPECT OTHERNUM
                    WHEN INTEGERNUMBER DO
                        ACC QUA REALNUMBER.REALVAL
                            := ACC QUA REALNUMBER.REALVAL + INTVAL
                    WHEN REALNUMBER DO
                        ACC QUA REALNUMBER.REALVAL
                            := ACC QUA REALNUMBER.REALVAL + REALVAL
                END;
            ACCUMULATE :- ACC;
        END ACCUMULATE;

        PROCEDURE OUT; ACC.OUT;

    END FOO;

    REF(ACCUMULATOR) FOO;
    FOO :- NEW ACCUMULATOR(NEW INTEGERNUMBER(1)); FOO.OUT; OUTIMAGE;
    FOO.ACCUMULATE(NEW INTEGERNUMBER(5));         FOO.OUT; OUTIMAGE;
    NEW ACCUMULATOR(NEW INTEGERNUMBER(3));
    FOO.ACCUMULATE(NEW REALNUMBER(2.3));          FOO.OUT; OUTIMAGE;

END.


Object subclass: AccumulatorFactory [
  AccumulatorFactory class >> new: aNumber [
    |r sum|
    sum := aNumber.
    r := [ :a |
           sum := sum +  a.
           sum
         ].
    ^r
  ]
]

|x y|
x := AccumulatorFactory new: 1.
x value: 5.
y := AccumulatorFactory new: 3.
(x value: 2.3) displayNl.
"x inspect."
"de-comment the previous line to show that x is a block closure"


fun accumulator (sum0:real) : real -> real = let
  val sum = ref sum0
  in
    fn n => (
      sum := !sum + n;
      !sum)
  end;

let
  val x = accumulator 1.0
  val _ = x 5.0
  val _ = accumulator 3.0
in
  print (Real.toString (x 2.3) ^ "\n")
end;


func makeAccumulator(var sum: Double) -> Double -> Double {
  return {
    sum += $0
    return sum
  }
}

let x = makeAccumulator(1)
x(5)
let _ = makeAccumulator(3)
println(x(2.3))


package require Tcl 8.6

# make the creation of coroutines without procedures simpler
proc coro {name arguments body args} {
    coroutine $name apply [list $arguments $body] {*}$args
}
# Wrap the feeding of values in and out of a generator
proc coloop {var body} {
    set val [info coroutine]
    upvar 1 $var v
    while 1 {
	set v [yield $val]
        if {$v eq "stop"} break
	set val [uplevel 1 $body]
    }
}

# The outer coroutine is the accumulator factory
# The inner coroutine is the particular accumulator
coro accumulator {} {
    coloop n {
	coro accumulator.[incr counter] n {
	    coloop i {
		set n [expr {$n + $i}]
	    }
	} $n
    }
}


(defun accumulate (sum)
  (lambda (n)
    (inc sum n)))

;; test
(for ((f (accumulate 0)) num)
     ((set num (iread : : nil)))
     ((format t "~s -> ~s\n" num [f num])))
(exit 0)


procedure main()
    a := genAcc(3)
    b := genAcc(5)

    write("        " ,center("a",5),  " ", center("b", 5))
    write("genAcc: ", right(a(4),5),  " ", right(b(4), 5))
    write("genAcc: ", right(a(2),5),  " ", right(b(3),5))
    write("genAcc: ", right(a(4.5),5)," ", right(b(1.3),5))
end

procedure genAcc(n)   # The generator factory
    return makeProc { while i := (n@&source)[1] do n +:= i }
end

procedure makeProc(A) # A Programmer-Defined Control Operation
    return (@A[1],A[1])
end


#!/bin/sh
accumulator() {
	# Define a global function named $1
	# with a global variable named ${1}_sum.
	eval "${1}_sum=\$2"
	eval "$1() {
		${1}_sum=\$(echo \"(\$${1}_sum) + (\$2)\" | bc)
		eval \"\$1=\\\$${1}_sum\"  # Provide the current sum.
	}"
}

accumulator x 1
x r 5
accumulator y 3
x r 2.3
echo $r
y r -3000
echo $r


class accumulator
	dim A
	public default function acc(x)
		A = A + x
		acc = A
	end function
	public property get accum
		accum = A
	end property
end class


var accumulator = Fn.new { |acc| Fn.new { |n| acc = acc + n } }

var x = accumulator.call(1)
x.call(5)
accumulator.call(3)
System.print(x.call(2.3))


; Accumulator factory
; Returns a function that returns the sum of all numbers ever passed in
; Build:
;   nasm -felf32 af.asm
;   ld -m elf32_i386 af.o -o af

global  _start
section .text

_start:
    mov eax, 0x2D       ; sys_brk(unsigned long brk)
    xor ebx, ebx        ; Returns current break on an error
    int 0x80            ; syscall
    push    eax         ; Save the initial program break

    push    2           ; Get an accumulator initialized to 2
    call    factory
    mov [acc1], eax     ; Save the pointer in acc1

    push    5           ; Get an accumulator initialized to 5
    call    factory
    mov [acc2], eax     ; Save the pointer in acc2

    push    4           ; Call acc1 with 4
    lea eax, [acc1]
    call    [eax]

    push    4           ; Call acc2 with 4
    lea eax, [acc2]
    call    [eax]

    push    -9          ; Call acc1 with -9
    lea eax, [acc1]
    call    [eax]

    push    13          ; Call acc1 with 13
    lea eax, [acc1]
    call    [eax]

    push    eax         ; Print the number, should be 10
    call    print_num

    push    -5          ; Call acc2 with -5
    lea eax, [acc2]
    call    [eax]

    push    eax         ; Print the number, should be 4
    call    print_num

    mov eax, 0x2D       ; Reset the program break
    pop ebx
    int 0x80

    mov eax, 0x01       ; sys_exit(int error)
    xor ebx, ebx        ; error = 0 (success)
    int 0x80

; int (*function)(int) factory (int n)
; Returns a pointer to a function that returns the sum of all numbers passed
; in to it, including the initial parameter n;
factory:
    push    ebp         ; Create stack frame
    mov ebp, esp
    push    ebx
    push    edi
    push    esi

    mov eax, 0x2D       ; Allocate memory for the accumulator
    xor ebx, ebx
    int 0x80
    push    eax         ; Save the current program break
    mov ebx, .acc_end   ; Calculate the new program break
    sub ebx, .acc
    push    ebx         ; Save the length
    add ebx, eax
    mov eax, 0x2D
    int 0x80

    pop ecx             ; Copy the accumulator code into memory
    pop eax             ; Set the returned address
    mov edi, eax
    mov esi, .acc
    rep movsb
    lea edi, [eax + 10] ; Copy the parameter to initialize accumulator
    lea esi, [ebp + 8]
    movsd

    pop esi             ; Tear down stack frame
    pop edi
    pop ebx
    mov esp, ebp
    pop ebp
    ret 4               ; Return and remove parameter from stack

.acc:                   ; Start of the returned accumulator
    push    ebp
    mov ebp, esp
    push    edi
    push    esi

    call    .acc_skip   ; Jumps over storage, pushing address to stack
    dd  0               ; The accumulator storage (32 bits)
.acc_skip:
    pop esi             ; Retrieve the accumulator using address on stack
    lodsd
    add eax, [ebp + 8]  ; Add the parameter
    lea edi, [esi - 4]
    stosd               ; Save the new value

    pop esi
    pop edi
    mov esp, ebp
    pop ebp
    ret 4
.acc_end:               ; End of accumulator

; void print_num (int n)
; Prints a positive integer and a newline
print_num:
    push    ebp
    mov ebp, esp

    mov eax, [ebp + 8]  ; Get the number
    lea ecx, [output + 10]  ; Put a newline at the end
    mov BYTE [ecx], 0x0A
    mov ebx, 10         ; Divisor
.loop:
    dec ecx             ; Move backwards in string
    xor edx, edx
    div ebx
    add edx, 0x30       ; Store ASCII digit
    mov [ecx], dl
    cmp eax, 0          ; Loop until all digits removed
    jnz .loop

    mov eax, 0x04       ; sys_write(int fd, char *buf, int len)
    mov ebx, 0x01       ; stdout
    lea edx, [output + 11]  ; Calulate length
    sub edx, ecx
    int 0x80

    mov esp, ebp
    pop ebp
    ret 4

section .bss
acc1:                   ; Variable that stores the first accumulator
    resd    1
acc2:                   ; Variable that stores the second accumulator
    resd    1
output:                 ; Holds the output buffer
    resb    11


(defun accumulator (x)
	(lambda (n)
		(setq x (+ n x))
		x ) )


sub foo$(n)
	local f$

	f$ = "f" + str$(int(ran(1000000)))
	compile("sub " + f$ + "(n): static acum : acum = acum + n : return acum : end sub")
	execute(f$, n)
	return f$
end sub

x$ = foo$(1)
execute(x$, 5)
foo$(3)
print execute(x$, 2.3)


func accum(data, n) {
   if(!is_obj(data))
      return closure(accum, save(total=data));
   save, data, total=data.total + n;
   return data.total;
}


fcn foo(n){ fcn(n,acc){ acc.set(n+acc.value).value }.fp1(Ref(n)) }


V txt = ‘Given$a$txt$file$of$many$lines,$where$fields$within$a$line$
are$delineated$by$a$single$'dollar'$character,$write$a$program
that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$
column$are$separated$by$at$least$one$space.
Further,$allow$for$each$word$in$a$column$to$be$either$left$
justified,$right$justified,$or$center$justified$within$its$column.’

V parts = txt.split("\n").map(line -> line.rtrim(‘$’).split(‘$’))
V max_widths = [0] * parts[0].len
L(line) parts
   L(word) line
      max_widths[L.index] = max(max_widths[L.index], word.len)

((String, Int) -> String) ljust = (s, w) -> s‘’(‘ ’ * (w - s.len))
((String, Int) -> String) centr = (s, w) -> (‘ ’ * (w - s.len - (w I/ 2 - s.len I/ 2)))‘’s‘’(‘ ’ * (w I/ 2 - s.len I/ 2))
((String, Int) -> String) rjust = (s, w) -> (‘ ’ * (w - s.len))‘’s

L(justify) [ljust, centr, rjust]
   print([‘Left’, ‘Center’, ‘Right’][L.index]‘ column-aligned output:’)
   L(line) parts
      L(word) line
         print(justify(word, max_widths[L.index]), end' ‘ ’)
      print()
   print(‘- ’ * 52)


*        Align columns             12/04/2019
ALICOL   CSECT
         USING  ALICOL,R13         base register
         B      72(R15)            skip savearea
         DC     17F'0'             savearea
         SAVE   (14,12)            save previous context
         ST     R13,4(R15)         link backward
         ST     R15,8(R13)         link forward
         LR     R13,R15            set addressability
         LA     R8,1               i=1
       DO WHILE=(C,R8,LE,=A(NI))   do r=1 to hbound(t)
         LA     R7,1                 j=1
         LA     R6,L'T               i=length(t)
       DO WHILE=(C,R6,GE,=A(1))      do i=length(t) to 1 by -1
         LR     R1,R8                  r
         MH     R1,=AL2(L'T)           ~
         LA     R4,T-L'T(R1)           t(r)
         BCTR   R4,0                   -1
         AR     R4,R6                  +i
         MVC    CI,0(R4)               ci=substr(t(r),i,1)
         CLI    CI,C' '                if ci=' '
         BE     ITERI1                 then iterate i
         CLI    CI,C'$'                if ci='$'
         BE     ITERI1                 then iterate i
         LR     R7,R6                  j=i
         B      LEAVEI1                leave i
ITERI1   BCTR   R6,0                   i--
       ENDDO    ,                    enddo i
LEAVEI1  LR     R1,R8                r
         MH     R1,=AL2(L'T)         ~
         LA     R4,T-L'T(R1)         @t(r)
         LA     R2,WT                @wt
         LR     R5,R7                j
         ICM    R5,B'1000',=C' '     padding
         LA     R3,L'T               length(wt)
         MVCL   R2,R4                wt=substr(t(r),1,j)
         LA     R0,1                 1
         ST     R0,I0                i0=1
         SR     R9,R9                c=0
         LA     R6,1                 i=1
       DO WHILE=(CR,R6,LE,R7)        do i=1 to j
         LA     R4,WT-1                @wt
         AR     R4,R6                  i
         MVC    CI(1),0(R4)            ci=substr(wt,i,1)
       IF   CLI,CI,EQ,C'$' THEN        if ci='$' then
         BAL    R14,SEQ                  call seq
         LR     R2,R6                    i
         LA     R2,1(R2)                 +1
         ST     R2,I0                    i0=i+1
       ENDIF    ,                      endif
         LA     R6,1(R6)               i++
       ENDDO    ,                    enddo i
         BAL    R14,SEQ            call seq
       IF     C,R9,GT,COLS THEN    if c>cols then
         ST     R9,COLS              cols=c
       ENDIF    ,                  endif
         LA     R8,1(R8)             r++
       ENDDO    ,                  enddo r
         LR     R2,R8              r
         BCTR   R2,0               -1
         ST     R2,ROWS            rows=r-1
         LA     R7,1               j=1
       DO WHILE=(C,R7,LE,=A(3))    do j=1 to 3
         XPRNT  =C'--',2             print
         LA     R8,1                 r=1
       DO WHILE=(C,R8,LE,ROWS)       do r=1 to rows
         MVC    PG,=CL120' '           pg=' '
         LA     R0,1                   1
         ST     R0,IB                  ib=1
         LA     R9,1                   c=1
       DO WHILE=(C,R9,LE,COLS)         do c=1 to cols
         LR     R1,R8                    r
         BCTR   R1,0                     -1
         MH     R1,=AL2(NJ)              ~
         AR     R1,R9                    c
         MH     R1,=AL2(L'WOR)           ~
         LA     R4,WOR-L'WOR(R1)         @wor(r,c)
         MVC    W,0(R4)                  w=wor(r,c)
         LA     R6,L'W                   i=length(w)
       DO WHILE=(C,R6,GE,=A(1))          do i=length(w) to 1 by -1
         LA     R4,W-1                     @w
         AR     R4,R6                      i
         MVC    CI,0(R4)                   ci=substr(w,i,1)
         CLI    CI,C' '                    if ci^=' '
         BNE    LEAVEI2                    then goto leavei2;
         BCTR   R6,0                       i--
       ENDDO    ,                        enddo i
LEAVEI2  EQU    *                        ~
       IF   LTR,R6,Z,R6 THEN             if i=0 then
         LA     R10,1                      l=1
       ELSE     ,                        else
         LR     R10,R6                     l=i
       ENDIF    ,                        endif
       IF     C,R7,EQ,=F'1' THEN         if j=1 then
         L      R11,IB                     ibx=ib
       ENDIF    ,                        endif
       IF     C,R7,EQ,=F'2' THEN         if j=2 then
         LR     R1,R9                      c
         SLA    R1,2                       ~
         L      R11,WID-L'WID(R1)          wid(c)
         A      R11,IB                     +ib
         SR     R11,R10                    ibx=ib+wid(c)-l
       ENDIF    ,                        endif
       IF     C,R7,EQ,=F'3' THEN         if j=3 then
         LR     R1,R9                      c
         SLA    R1,2                       ~
         L      R11,WID-L'WID(R1)          wid(c)
         SR     R11,R10                    -l
         SRA    R11,1                      /2
         A      R11,IB                     ibx=ib+(wid(c)-l)/2
       ENDIF    ,                        endif
         LA     R2,PG-1                  @pg
         AR     R2,R11                   +ibx
         LR     R3,R10                   l
         LA     R4,W                     @w
         LR     R5,R10                   l
         MVCL   R2,R4                    substr(pg,ibx,l)=substr(w,1,l)
         LR     R1,R9                    c
         SLA    R1,2                     ~
         L      R2,WID-L'WID(R1)         wid(c)
         A      R2,IB                    +ib
         LA     R2,1(R2)                 +1
         ST     R2,IB                    ib=ib+wid(c)+1
         LA     R9,1(R9)                 c++
       ENDDO    ,                      enddo c
         XPRNT  PG,L'PG                print
         LA     R8,1(R8)               r++
       ENDDO    ,                    enddo r
         LA     R7,1(R7)             j++
       ENDDO    ,                  enddo j
         L      R13,4(0,R13)       restore previous savearea pointer
         RETURN (14,12),RC=0       restore registers from calling sav
SEQ      EQU    *                  --begin seq
         LA     R9,1(R9)           c=c+1
         LR     R10,R6             i
         S      R10,I0             l=i-i0
         LA     R4,WT-1            @wt
         A      R4,I0              +i0
         LR     R5,R10             l
         ICM    R5,B'1000',=C' '   padding
         LR     R1,R8              r
         BCTR   R1,0               -1
         MH     R1,=AL2(NJ)        ~
         AR     R1,R9              +c
         MH     R1,=AL2(L'WOR)     ~
         LA     R2,WOR-L'WOR(R1)   @wor(r,c)
         LA     R3,L'WOR           length(wor)
         MVCL   R2,R4              wor(r,c)=substr(wt,i0,l)
         LR     R1,R9              c
         SLA    R1,2               ~
         L      R2,WID-L'WID(R1)   wid(c)
       IF     CR,R2,LT,R10 THEN    if l>wid(c) then
         LR     R1,R9                c
         SLA    R1,2                 ~
         ST     R10,WID-L'WID(R1)    wid(c)=l
       ENDIF    ,                  endif
         BR     R14                --end seq
NI       EQU    6                  ni
NJ       EQU    12                 nj
T DC CL68'Given$a$text$file$of$many$lines,$where$fields$within$a$line$'
  DC CL68'are$delineated$by$a$single$''dollar''$character,$write$a$progX
               ramm'
  DC CL68'that$aligns$each$column$of$fields$by$ensuring$that$words$in$eX
               ach$'
  DC CL68'column$are$separated$by$at$least$one$space.'
  DC CL68'Further,$allow$for$each$word$in$a$column$to$be$either$left$'
  DC CL68'justified,$right$justified,$or$center$justified$within$its$coX
               lumn.'
WOR      DC     (NI*NJ)CL10' '     wor(ni,nj) char(10)
WID      DC     16F'0'             wid(16)
COLS     DC     F'0'
ROWS     DC     F'0'
WT       DS     CL(L'T)
W        DS     CL(L'WOR)
CI       DS     CL1
I0       DS     F
IB       DS     F
PG       DS     CL120
         REGEQU
         END    ALICOL


quote | Given$a$text$file$of$many$lines,$where$fields$within$a$line$
are$delineated$by$a$single$'dollar'$character,$write$a$program
that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$
column$are$separated$by$at$least$one$space.
Further,$allow$for$each$word$in$a$column$to$be$either$left$
justified,$right$justified,$or$center$justified$within$its$column.|
var, raw-text

[] var, data
var width

: read-and-parse \ --
  raw-text @
  ( "$" s:/ data @ swap a:push drop )
  s:eachline ;

: find-widest \ -- n
  data @ ( ( swap s:len nip n:max ) swap a:reduce ) 0 a:reduce ;

: print-data \ fmt --
  width @ swap s:strfmt >r
  data @
  (
    nip
    (
      nip
      [email protected] s:strfmt .
    ) a:each drop

    cr
  ) a:each drop rdrop ;

: app:main
  read-and-parse

  \ find widest column, and add one for the space:
  find-widest n:1+ width !

  \ print the data
  cr "right:" . cr "%%>%ds" print-data
  cr "left:" . cr "%%<%ds" print-data
  cr "center:" . cr "%%|%ds" print-data
  bye ;


/* ARM assembly AARCH64 Raspberry PI 3B or android 64 bits */
/*  program alignColumn64.s     */

/*******************************************/
/* Constantes file                         */
/*******************************************/
/* for this file see task include a file in language AArch64 assembly*/
.include "../includeConstantesARM64.inc"

.equ BUFFERSIZE, 16 * 10

/*********************************/
/* Initialized data              */
/*********************************/
.data
szMessLeft:        .asciz "LEFT :\n"
szMessRight:       .asciz "\nRIGHT :\n"
szMessCenter:      .asciz "\nCENTER :\n"
szCarriageReturn:  .asciz "\n"

szLine1:    .asciz "Given$a$text$file$of$many$lines,$where$fields$within$a$line$"
szLine2:    .asciz "are$delineated$by$a$single$'dollar'$character,$write$a$program"
szLine3:    .asciz "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$"
szLine4:    .asciz "column$are$separated$by$at$least$one$space."
szLine5:    .asciz "Further,$allow$for$each$word$in$a$column$to$be$either$left$"
szLine6:    .asciz "justified,$right$justified,$or$center$justified$within$its$column."

.align 8
qtbPtLine:    .quad szLine1,szLine2,szLine3,szLine4,szLine5,szLine6
              .equ NBLINES, (. - qtbPtLine) / 8

/*********************************/
/* UnInitialized data            */
/*********************************/
.bss
/*********************************/
/*  code section                 */
/*********************************/
.text
.global main
main:                               // entry of program
    ldr x0,qAdrqtbPtLine
    bl computeMaxiLengthWords
    mov x19,x0                      // column counter
    ldr x0,qAdrszMessLeft
    bl affichageMess
    ldr x0,qAdrqtbPtLine
    mov x1,x19                      // column size
    bl alignLeft
    ldr x0,qAdrszMessRight
    bl affichageMess
    ldr x0,qAdrqtbPtLine
    mov x1,x19                      // column size
    bl alignRight
    ldr x0,qAdrszMessCenter
    bl affichageMess
    ldr x0,qAdrqtbPtLine
    mov x1,x19                      // column size
    bl alignCenter
100:                                // standard end of the program
    mov x0, #0                      // return code
    mov x8, #EXIT                   // request to exit program
    svc #0                          // perform the system call
qAdrszCarriageReturn:     .quad szCarriageReturn
qAdrszMessLeft:           .quad szMessLeft
qAdrszMessRight:          .quad szMessRight
qAdrszMessCenter:         .quad szMessCenter
qAdrqtbPtLine:            .quad qtbPtLine
/******************************************************************/
/*      compute maxi words                                 */
/******************************************************************/
/* x0 contains adresse pointer array */
computeMaxiLengthWords:
    stp fp,lr,[sp,-16]!      // save  registres
    mov x2,#0                // indice pointer array
    mov x3,#0                // maxi length words
1:
    ldr x1,[x0,x2,lsl #3]    // load pointer
    mov x4,#0                // length words counter
    mov x5,#0                // indice line character
2:
    ldrb w6,[x1,x5]          // load a line character
    cmp x6,#0                // line end ?
    beq 4f
    cmp x6,#'$'              // separator ?
    bne 3f
    cmp x4,x3
    csel x3,x4,x3,gt         // if greather replace maxi
    mov x4,#-1               // raz length counter
3:
    add x4,x4,#1
    add x5,x5,#1             // increment character indice
    b 2b                     // and loop
4:                           // end line
    cmp x4,x3                // compare word counter and maxi
    csel x3,x4,x3,gt         // if greather replace maxi
    add x2,x2,#1             // increment indice line pointer
    cmp x2,#NBLINES          // maxi ?
    blt 1b                   // no -> loop

    mov x0,x3                // return maxi length counter
100:
    ldp fp,lr,[sp],16        // restaur des  2 registres
    ret

/******************************************************************/
/*      align left                                                */
/******************************************************************/
/* x0 contains the address of pointer array*/
/* x1 contains column size */
alignLeft:
    stp fp,lr,[sp,-16]!       // save  registres
    sub sp,sp,#BUFFERSIZE    // reserve place for output buffer
    mov fp,sp
    mov x5,x0                // array address
    mov x2,#0                // indice array
1:
    ldr x3,[x5,x2,lsl #3]    // load line pointer
    mov x4,#0                // line character indice
    mov x7,#0                // output buffer character indice
    mov x6,#0                // word lenght
2:
    ldrb w0,[x3,x4]          // load a character line
    strb w0,[fp,x7]          // store in buffer
    cmp w0,#0                // line end ?
    beq 6f
    cmp w0,#'$'              // separator ?
    bne 5f
    mov x0,#' '
    strb w0,[fp,x7]          // replace $ by space
3:
    cmp x6,x1                // length word >= length column
    bge 4f
    add x7,x7,#1
    mov x0,#' '
    strb w0,[fp,x7]          // add space to buffer
    add x6,x6,#1
    b 3b                     // and loop
4:
    mov x6,#-1               // raz word length
5:
    add x4,x4,#1             // increment line indice
    add x7,x7,#1             // increment buffer indice
    add x6,x6,#1             // increment word length
    b 2b

6:
    mov x0,#'\n'
    strb w0,[fp,x7]          // return line
    add x7,x7,#1
    mov x0,#0
    strb w0,[fp,x7]          // final zéro
    mov x0,fp
    bl affichageMess         // display output buffer
    add x2,x2,#1
    cmp x2,#NBLINES
    blt 1b

100:
    add sp,sp,#BUFFERSIZE
    ldp fp,lr,[sp],16        // restaur des  2 registres
    ret
/******************************************************************/
/*      align right                                                */
/******************************************************************/
/* x0 contains the address of pointer array*/
/* x1 contains column size */
alignRight:
    stp fp,lr,[sp,-16]!       // save  registres
    sub sp,sp,#BUFFERSIZE    // reserve place for output buffer
    mov fp,sp
    mov x5,x0                // array address
    mov x2,#0                // indice array
1:
    ldr x3,[x5,x2,lsl #3]    // load line pointer
    mov x4,#0                // line character indice
    mov x7,#0                // output buffer character indice
    mov x6,#0                // word lenght
    mov x8,x3                // word begin address
2:                           // compute word length
    ldrb w0,[x3,x4]          // load a character line
    cmp w0,#0                // line end ?
    beq 3f
    cmp w0,#'$'              // separator ?
    beq 3f
    add x4,x4,#1             // increment line indice
    add x6,x6,#1             // increment word length
    b 2b

3:
    cmp x6,#0
    beq 4f
    sub x6,x1,x6             // compute left spaces to add
4:                           // loop add spaces to buffer
    cmp x6,#0
    blt 5f
    mov x0,#' '
    strb w0,[fp,x7]          // add space to buffer
    add x7,x7,#1
    sub x6,x6,#1
    b 4b                     // and loop
5:
    mov x9,#0
6:                           // copy loop word to buffer
    ldrb w0,[x8,x9]
    cmp x0,#'$'
    beq 7f
    cmp x0,#0                // line end
    beq 8f
    strb w0,[fp,x7]
    add x7,x7,#1
    add x9,x9,#1
    b 6b
7:
    add x8,x8,x9
    add x8,x8,#1             // new word begin
    mov x6,#0                // raz word length
    add x4,x4,#1             // increment line indice
    b 2b

8:
    mov x0,#'\n'
    strb w0,[fp,x7]          // return line
    add x7,x7,#1
    mov x0,#0
    strb w0,[fp,x7]          // final zéro
    mov x0,fp
    bl affichageMess         // display output buffer
    add x2,x2,#1
    cmp x2,#NBLINES
    blt 1b

100:
    add sp,sp,#BUFFERSIZE
    ldp fp,lr,[sp],16        // restaur des  2 registres
    ret
/******************************************************************/
/*      align center                                                */
/******************************************************************/
/* x0 contains the address of pointer array*/
/* x1 contains column size */
alignCenter:
    stp fp,lr,[sp,-16]!      // save  registres
    sub sp,sp,#BUFFERSIZE    // reserve place for output buffer
    mov fp,sp
    mov x5,x0                // array address
    mov x2,#0                // indice array
1:
    ldr x3,[x5,x2,lsl #3]    // load line pointer
    mov x4,#0                // line character indice
    mov x7,#0                // output buffer character indice
    mov x6,#0                // word length
    mov x8,x3                // word begin address
2:                           // compute word length
    ldrb w0,[x3,x4]          // load a character line
    cmp w0,#0                // line end ?
    beq 3f
    cmp w0,#'$'              // separator ?
    beq 3f
    add x4,x4,#1             // increment line indice
    add x6,x6,#1             // increment word length
    b 2b
3:
    cmp x6,#0
    beq 5f
    sub x6,x1,x6             // total spaces number to add
    mov x12,x6
    lsr x6,x6,#1             // divise by 2 = left spaces number
4:
    cmp x6,#0
    blt 5f
    mov x0,#' '
    strb w0,[fp,x7]          // add space to buffer
    add x7,x7,#1             // increment output indice
    sub x6,x6,#1             // decrement number space
    b 4b                     // and loop
5:
    mov x9,#0
6:                           // copy loop word to buffer
    ldrb w0,[x8,x9]
    cmp x0,#'$'              // séparator ?
    beq 7f
    cmp x0,#0                // line end ?
    beq 10f
    strb w0,[fp,x7]
    add x7,x7,#1
    add x9,x9,#1
    b 6b
7:
    lsr x6,x12,#1            // divise total spaces by 2
    sub x6,x12,x6            // and compute number spaces to right side
8:                           // loop to add right spaces
    cmp x6,#0
    ble 9f
    mov x0,#' '
    strb w0,[fp,x7]          // add space to buffer
    add x7,x7,#1
    sub x6,x6,#1
    b 8b                     // and loop

9:
    add x8,x8,x9
    add x8,x8,#1             // new address word begin
    mov x6,#0                // raz word length
    add x4,x4,#1             // increment line indice
    b 2b                     // and loop new word

10:
    mov x0,#'\n'
    strb w0,[fp,x7]          // return line
    add x7,x7,#1
    mov x0,#0
    strb w0,[fp,x7]          // final zéro
    mov x0,fp
    bl affichageMess         // display output buffer
    add x2,x2,#1             // increment line indice
    cmp x2,#NBLINES          // maxi ?
    blt 1b                   // loop

100:
    add sp,sp,#BUFFERSIZE
    ldp fp,lr,[sp],16        // restaur des  2 registres
    ret
/********************************************************/
/*        File Include fonctions                        */
/********************************************************/
/* for this file see task include a file in language AArch64 assembly */
.include "../includeARM64.inc"


report z_align no standard page header.
start-of-selection.

data: lt_strings type standard table of string,
      lv_strings type string.
append: 'Given$a$text$file$of$many$lines,$where$fields$within$a$line$' to lt_strings,
        'are$delineated$by$a$single$''dollar''$character,$write$a$program' to lt_strings,
        'that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$' to lt_strings,
        'column$are$separated$by$at$least$one$space.' to lt_strings,
        'Further,$allow$for$each$word$in$a$column$to$be$either$left$' to lt_strings,
        'justified,$right$justified,$or$center$justified$within$its$column.' to lt_strings.
types ty_strings type standard table of string.

perform align_col using 'LEFT' lt_strings.
skip.
perform align_col using 'RIGHT' lt_strings.
skip.
perform align_col using 'CENTER' lt_strings.

form align_col using iv_just type string iv_strings type ty_strings.
  constants: c_del value '$'.
  data: lv_string type string,
        lt_strings type table of string,
        lt_tables like table of lt_strings,
        lv_first type string,
        lv_second type string,
        lv_longest type i value 0,
        lv_off type i value 0,
        lv_len type i.
  " Loop through the supplied text. It is expected at the input is a table of strings, with each
  " entry in the table representing a new line of the input.
  loop at iv_strings into lv_string.
    " Split the current line at the delimiter.
    split lv_string at c_del into lv_first lv_second.
    " Loop through the line splitting at every delimiter.
    do.
      append lv_first to lt_strings.
      lv_len = strlen( lv_first ).
      " Check if the length of the new string is greater than the currently stored length.
      if lv_len > lv_longest.
        lv_longest = lv_len.
      endif.
      if lv_second na c_del.
        " Check if the string is longer than the recorded maximum.
        lv_len = strlen( lv_second ).
        if lv_len > lv_longest.
          lv_longest = lv_len.
        endif.
        append lv_second to lt_strings.
        exit.
      endif.
      split lv_second at c_del into lv_first lv_second.
    enddo.

    append lt_strings to lt_tables.
    clear lt_strings.
  endloop.

  " Loop through each line of input.
  loop at lt_tables into lt_strings.
    " Loop through each word in the line (Separated by specified delimiter).
    loop at lt_strings into lv_string.
      lv_off = ( sy-tabix - 1 ) * ( lv_longest + 2 ).
      case iv_just.
        when 'LEFT'.
          write : at (lv_longest) lv_string left-justified.
        when 'RIGHT'.
          write at (lv_longest) lv_string right-justified.
        when 'CENTER'.
          write at (lv_longest) lv_string centered.
      endcase.
    endloop.
    skip.
    sy-linno = sy-linno - 1.
  endloop.
endform.


DEFINE LINES_COUNT="10"
DEFINE COLUMNS_COUNT="20"
DEFINE WORDS_COUNT="100"
DEFINE BUFFER_SIZE="2000"
DEFINE LINE_WIDTH="40"
DEFINE PTR="CARD"

PTR ARRAY lines(LINES_COUNT)
BYTE ARRAY wordStart(WORDS_COUNT)
BYTE ARRAY wordLen(WORDS_COUNT)
BYTE ARRAY firstWordInLine(LINES_COUNT)
BYTE ARRAY wordsInLine(LINES_COUNT)
BYTE ARRAY colWidths(COLUMNS_COUNT)
BYTE ARRAY buffer(BUFFER_SIZE)
BYTE lineCount,colCount,wordCount

CHAR sep=['$]

PROC AddLine(CHAR ARRAY line)
  lines(lineCount)=line
  lineCount==+1
RETURN

PROC InitData()
  lineCount=0
  AddLine("Given$a$text$file$of$many$lines,$where$fields$within$a$line$")
  AddLine("are$delineated$by$a$single$'dollar'$character,$write$a$program")
  AddLine("that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$")
  AddLine("column$are$separated$by$at$least$one$space.")
  AddLine("Further,$allow$for$each$word$in$a$column$to$be$either$left$")
  AddLine("justified,$right$justified,$or$center$justified$within$its$column.")
RETURN

PROC ProcessData()
  BYTE i,pos,len,start,w,col
  CHAR ARRAY line

  colCount=0 wordCount=0
  FOR i=0 TO lineCount-1
  DO
    line=lines(i)
    len=line(0)
    firstWordInLine(i)=wordCount
    wordsInLine(i)=0
    pos=1 col=0
    WHILE pos<=len
    DO
      start=pos
      WHILE pos<=len AND line(pos)#sep
      DO pos==+1 OD
      w=pos-start
      wordStart(wordCount)=start
      wordLen(wordCount)=w
      wordCount==+1
      wordsInLine(i)==+1
      IF col=colCount THEN
        colWidths(col)=w
        colCount==+1
      ELSEIF w>colWidths(col) THEN
        colWidths(col)=w
      FI
      col==+1
      pos==+1
    OD
  OD
RETURN

BYTE FUNC GetBufLineLength()
  BYTE i,res

  res=0
  FOR i=0 TO colCount-1
  DO
    res==+colWidths(i)+1
  OD
  res==+1
RETURN (res)

BYTE FUNC AtasciiToInternal(CHAR c)
  BYTE c2

  c2=c&$7F
  IF c2<32 THEN
    RETURN (c+64)
  ELSEIF c2<96 THEN
    RETURN (c-32)
  FI
RETURN (c)

PROC GenerateLine(BYTE index BYTE align BYTE POINTER p)
  BYTE wordIndex,last,left,right,start,len,colW
  INT i,j
  CHAR ARRAY line

  line=lines(index)
  wordIndex=firstWordInLine(index)
  last=wordIndex+wordsInLine(index)-1
  FOR i=0 TO colCount-1
  DO
    colW=colWidths(i)

    p^=124 p==+1
    IF wordIndex<=last THEN
      start=wordStart(wordIndex)
      len=wordLen(wordIndex)

      IF align=0 THEN
        left=0
        right=colW-len
      ELSEIF align=1 THEN
        left=(colW-len)/2
        right=colW-left-len
      ELSE
        left=colW-len
        right=0
      FI

      p==+left
      for j=start TO start+len-1
      DO
        p^=AtasciiToInternal(line(j))
        p==+1
      OD
      p==+right
    ELSE
      p==+colW
    FI

    wordIndex==+1
  OD
  p^=124
RETURN

PROC FillBuffer(BYTE lineWidth)
  BYTE i,align
  BYTE POINTER p

  p=buffer
  Zero(p,BUFFER_SIZE)
  FOR align=0 TO 2
  DO
    FOR i=0 TO lineCount-1
    DO
      GenerateLine(i,align,p)
      p==+lineWidth
    OD
  OD
RETURN

BYTE FUNC GetMaxOffset()
  BYTE res

  res=GetBufLineLength()-LINE_WIDTH
RETURN (res)

PROC Update(BYTE offset,lineWidth)
  BYTE POINTER srcPtr,dstPtr
  BYTE i

  srcPtr=buffer+offset
  dstPtr=PeekC(88)+3*LINE_WIDTH
  FOR i=0 TO 3*lineCount-1
  DO
    MoveBlock(dstPtr,srcPtr,LINE_WIDTH)
    srcPtr==+lineWidth
    dstPtr==+LINE_WIDTH
    IF i=lineCount-1 OR i=2*lineCount-1 THEN
      dstPtr==+LINE_WIDTH
    FI
  OD
RETURN

PROC Main()
  BYTE
    lineWidth,offset,maxOffset,k,
    CH=$02FC, ;Internal hardware value for last key pressed
    CRSINH=$02F0 ;Controls visibility of cursor

  CRSINH=1 ;hide cursor

  InitData()
  ProcessData()
  lineWidth=GetBufLineLength()
  FillBuffer(lineWidth)

  Position(2,1)
  Print("Press left/right arrow key to scroll")

  maxOffset=lineWidth-LINE_WIDTH
  offset=0
  Update(offset,lineWidth)
  DO
    k=CH
    IF k#$FF THEN
      CH=$FF
    FI

    IF k=134 AND offset>0 THEN
      offset==-1
      Update(offset,lineWidth)
    ELSEIF k=135 AND offset<maxOffset THEN
      offset==+1
      Update(offset,lineWidth)
    ELSEIF k=28 THEN
      EXIT
    FI
  OD

RETURN


with Ada.Characters.Latin_1;  use Ada.Characters.Latin_1;
with Ada.Text_IO;             use Ada.Text_IO;
with Strings_Edit;            use Strings_Edit;

procedure Column_Aligner is
   Text : constant String :=
      "Given$a$text$file$of$many$lines,$where$fields$within$a$line$" & NUL &
      "are$delineated$by$a$single$'dollar'$character,$write$a$program" & NUL &
      "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$" & NUL &
      "column$are$separated$by$at$least$one$space." & NUL &
      "Further,$allow$for$each$word$in$a$column$to$be$either$left$" & NUL &
      "justified,$right$justified,$or$center$justified$within$its$column." & NUL;
   File    : File_Type;
   Width   : array (1..1_000) of Natural := (others => 0);
   Line    : String (1..200);
   Column  : Positive := 1;
   Start   : Positive := 1;
   Pointer : Positive;
begin
   Create (File, Out_File, "columned.txt");
      -- Determining the widths of columns
   for I in Text'Range loop
      case Text (I) is
         when '$' | NUL =>
            Width (Column) := Natural'Max (Width (Column), I - Start + 1);
            Start  := I + 1;
            if Text (I) = NUL then
               Column := 1;
            else
               Column := Column + 1;
            end if;
         when others =>
            null;
      end case;
   end loop;
      -- Formatting
   for Align in Alignment loop
      Column  := 1;
      Start   := 1;
      Pointer := 1;
      for I in Text'Range loop
         case Text (I) is
            when '$' | NUL =>
               Put -- Formatted output of a word
               (  Destination => Line,
                  Pointer     => Pointer,
                  Value       => Text (Start..I - 1),
                  Field       => Width (Column),
                  Justify     => Align
               );
               Start  := I + 1;
               if Text (I) = NUL then
                  Put_Line (File, Line (1..Pointer - 1));
                  Pointer := 1;
                  Column := 1;
               else
                  Column := Column + 1;
               end if;
            when others =>
               null;
         end case;
      end loop;
   end loop;
   Close (File);
end Column_Aligner;


data b;
file f;
text n, t;
list c, r, s;
integer a, i, k, m, w;

b = "Given$a$text$file$of$many$lines,$where$fields$within$a$line$\n"
    "are$delineated$by$a$single$'dollar'$character,$write$a$program\n"
    "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$\n"
    "column$are$separated$by$at$least$one$space.\n"
    "Further,$allow$for$each$word$in$a$column$to$be$either$left$\n"
    "justified,$right$justified,$or$center$justified$within$its$column.";

f.b_affix(b);

m = 0;

while (f.news(r, 0, 0, "$") ^ -1) {
    c.append(r);
    m = max(m, ~r);
}

i = 0;
while (i < m) {
    w = 0;
    for (, r in c) {
        if (i < ~r) {
            w = max(w, length(r[i]));
        }
    }
    s.append(w + 1);
    i += 1;
}

for (k, t in list("left", "center", "right")) {
    o_(t, " justified\n");
    for (, r in c) {
        for (i, n in r) {
            m = s[i] - ~n;
            o_form("/w~3/~/w~1/", a = (2 - k) * m >> 1, "", m - a, "", n);
        }
        o_newline();
    }
    o_newline();
}


STRING nl = REPR 10;
STRING text in list := "Given$a$text$file$of$many$lines,$where$fields$within$a$line$"+nl+
  "are$delineated$by$a$single$'dollar'$character,$write$a$program"+nl+
  "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$"+nl+
  "column$are$separated$by$at$least$one$space."+nl+
  "Further,$allow$for$each$word$in$a$column$to$be$either$left$"+nl+
  "justified,$right$justified,$or$center$justified$within$its$column.";

MODE PAGE = FLEX[0,0]STRING;
PAGE page;

PROC flex page = (PAGE in page, INT row, col)PAGE:(
  HEAP FLEX[row, col]STRING out page;
  out page[:1 UPB in page, :2 UPB in page] := in page;
  FOR r TO row DO
    FOR c FROM 2 UPB in page + 1 TO col DO out page[r,c]:="" OD
  OD;
  FOR r FROM 1 UPB in page + 1 TO row DO
    FOR c FROM 1 TO col DO out page[r,c]:="" OD
  OD;
  out page
);

FILE text in file;
associate(text in file, text in list);
make term(text in file, "$");

on physical file end(text in file, (REF FILE skip)BOOL: stop iteration);
on logical file end(text in file, (REF FILE skip)BOOL: stop iteration);
FOR row DO
  on line end(text in file, (REF FILE skip)BOOL: stop iteration);
  FOR col DO
    STRING tok;
    getf(text in file, ($gx$,tok));
    IF row > 1 UPB page THEN page := flex page(page, row, 2 UPB page) FI;
    IF col > 2 UPB page THEN page := flex page(page, 1 UPB page, col) FI;
    page[row,col]:=tok
  OD;
  stop iteration:
    SKIP
OD;
stop iteration:
  SKIP;

BEGIN
  PROC aligner = (PAGE in page, PROC (STRING,INT)STRING aligner)VOID:(
    PAGE page := in page;
    [2 UPB page]INT max width;
    FOR col TO 2 UPB page DO
      INT max len:=0; FOR row TO UPB page DO IF UPB page[row,col]>max len THEN max len:=UPB page[row,col] FI OD;
      FOR row TO UPB page DO page[row,col] := aligner(page[row,col], maxlen) OD
    OD;
    printf(($n(UPB page)(n(2 UPB page -1)(gx)gl)$,page))
  );

  PROC left = (STRING in, INT len)STRING: in + " "*(len - UPB in),
       right = (STRING in, INT len)STRING: " "*(len - UPB in) + in,
       centre = (STRING in, INT len)STRING: ( INT pad=len-UPB in;  pad%2*" "+ in + (pad-pad%2)*" " );

  []STRUCT(STRING name, PROC(STRING,INT)STRING align) aligners = (("Left",left), ("Left",right), ("Centre",centre));

  FOR index TO UPB aligners DO
    print((new line, "# ",name OF aligners[index]," Column-aligned output:",new line));
    aligner(page, align OF aligners[index])
  OD
END


#define IGet(__N__,__X__)  [__N__]SGet(__X__)

#include <hbasic.h>
#define MAX_LINE  1000

Begin
   Option Stack 15

   Declare as Numeric ( fd, i, index, max token )
           as Numeric ( num tokens, size Column, tCells )
           as Alpha ( line )
           as Numeric ( display Left, display Right, display Center )

   GetParam(script, file to edit, separator)

  // get statistics of file: #lines, #total chars, line more long, and num tokens from first line.
   Token Sep( separator )
   Stat( file to edit, dats )

  // declare arrays to work:
   Dim ( IGet(1,dats), Add(IGet(4,dats),10) ) for Fill Array("",cells)
   Clear(dats)
   MStore ( cells, display Left, display Right, display Center )

  // read each line as array, get # of elements, and put into array cells:
   Open(OPEN_READ, file to edit ) (fd)
   When( File Error ){ Stop }

   index=1
   While Not Eof(fd)
      ReadRow(MAX_LINE)(fd) and Copy to (line); get Length, and Move to (num tokens)
      Set Interval [index, 1:num tokens]; Take( line ) and SPut(cells)
      When ( var( max token) Is Lt (num tokens) ) { max token = num tokens }
      ++index
   Wend
   Close(fd)

  // formatting...
   For Up( i:=1, max token, 1 )
      Set Interval [1:end,i], and Let ( size Column := MaxValue( Len(Get(cells)) ) Plus(1) )
      Let ( tCells := Get(cells) )
      LPad$( " ", size Column, tCells ), and Put(display Left)
      RPad$( " ", size Column, tCells ), and Put(display Right)
      CPad$( " ", size Column, tCells ), and Put(display Center)
   Next

  // display:
   Token Sep ("")
   Print("Left Pad:\n", display Left, Newl, "Right Pad:\n", display Right, Newl, "Center Pad:\n", display Center,Newl)
End


-- COLUMN ALIGNMENTS ---------------------------------------------------------

property pstrLines : ¬
    "Given$a$text$file$of$many$lines,$where$fields$within$a$line$\n" & ¬
    "are$delineated$by$a$single$'dollar'$character,$write$a$program\n" & ¬
    "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$\n" & ¬
    "column$are$separated$by$at$least$one$space.\n" & ¬
    "Further,$allow$for$each$word$in$a$column$to$be$either$left$\n" & ¬
    "justified,$right$justified,$or$center$justified$within$its$column."

property eLeft : -1
property eCenter : 0
property eRight : 1

-- columnsAligned :: EnumValue -> [[String]] -> String
on columnsAligned(eAlign, lstCols)
    -- padwords :: Int -> [String] -> [[String]]
    script padwords
        on |λ|(n, lstWords)

            -- pad :: String -> String
            script pad
                on |λ|(str)
                    set lngPad to n - (length of str)
                    if eAlign = my eCenter then
                        set lngHalf to lngPad div 2
                        {replicate(lngHalf, space), str, ¬
                            replicate(lngPad - lngHalf, space)}
                    else
                        if eAlign = my eLeft then
                            {"", str, replicate(lngPad, space)}
                        else
                            {replicate(lngPad, space), str, ""}
                        end if
                    end if
                end |λ|
            end script

            map(pad, lstWords)
        end |λ|
    end script

    unlines(map(my unwords, ¬
        transpose(zipWith(padwords, ¬
            map(my widest, lstCols), lstCols))))
end columnsAligned

-- lineColumns :: String -> String -> String
on lineColumns(strColDelim, strText)
    -- _words :: Text -> [Text]
    script _words
        on |λ|(str)
            splitOn(strColDelim, str)
        end |λ|
    end script

    set lstRows to map(_words, splitOn(linefeed, pstrLines))
    set nCols to widest(lstRows)

    -- fullRow :: [[a]] -> [[a]]
    script fullRow
        on |λ|(lst)
            lst & replicate(nCols - (length of lst), {""})
        end |λ|
    end script

    transpose(map(fullRow, lstRows))
end lineColumns

-- widest [a] -> Int
on widest(xs)
    |length|(maximumBy(comparing(my |length|), xs))
end widest

-- TEST ----------------------------------------------------------------------
on run
    set lstCols to lineColumns("$", pstrLines)

    script testAlignment
        on |λ|(eAlign)
            columnsAligned(eAlign, lstCols)
        end |λ|
    end script

    intercalate(return & return, ¬
        map(testAlignment, {eLeft, eRight, eCenter}))
end run

-- GENERIC FUNCTIONS ---------------------------------------------------------

-- comparing :: (a -> b) -> (a -> a -> Ordering)
on comparing(f)
    set mf to mReturn(f)
    script
        on |λ|(a, b)
            set x to mf's |λ|(a)
            set y to mf's |λ|(b)
            if x < y then
                -1
            else
                if x > y then
                    1
                else
                    0
                end if
            end if
        end |λ|
    end script
end comparing

-- foldl :: (a -> b -> a) -> a -> [b] -> a
on foldl(f, startValue, xs)
    tell mReturn(f)
        set v to startValue
        set lng to length of xs
        repeat with i from 1 to lng
            set v to |λ|(v, item i of xs, i, xs)
        end repeat
        return v
    end tell
end foldl

-- Text -> [Text] -> Text
on intercalate(strText, lstText)
    set {dlm, my text item delimiters} to {my text item delimiters, strText}
    set strJoined to lstText as text
    set my text item delimiters to dlm
    return strJoined
end intercalate

-- length :: [a] -> Int
on |length|(xs)
    length of xs
end |length|

-- Lift 2nd class handler function into 1st class script wrapper
-- mReturn :: Handler -> Script
on mReturn(f)
    if class of f is script then
        f
    else
        script
            property |λ| : f
        end script
    end if
end mReturn

-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
    tell mReturn(f)
        set lng to length of xs
        set lst to {}
        repeat with i from 1 to lng
            set end of lst to |λ|(item i of xs, i, xs)
        end repeat
        return lst
    end tell
end map

-- maximumBy :: (a -> a -> Ordering) -> [a] -> a
on maximumBy(f, xs)
    set cmp to mReturn(f)
    script max
        on |λ|(a, b)
            if a is missing value or cmp's |λ|(a, b) < 0 then
                b
            else
                a
            end if
        end |λ|
    end script

    foldl(max, missing value, xs)
end maximumBy

-- min :: Ord a => a -> a -> a
on min(x, y)
    if y < x then
        y
    else
        x
    end if
end min

-- Egyptian multiplication - progressively doubling a list, appending
-- stages of doubling to an accumulator where needed for binary
-- assembly of a target length

-- replicate :: Int -> a -> [a]
on replicate(n, a)
    if class of a is string then
        set out to ""
    else
        set out to {}
    end if
    if n < 1 then return out
    set dbl to a

    repeat while (n > 1)
        if (n mod 2) > 0 then set out to out & dbl
        set n to (n div 2)
        set dbl to (dbl & dbl)
    end repeat
    return out & dbl
end replicate

-- Text -> Text -> [Text]
on splitOn(strDelim, strMain)
    set {dlm, my text item delimiters} to {my text item delimiters, strDelim}
    set lstParts to text items of strMain
    set my text item delimiters to dlm
    return lstParts
end splitOn

-- transpose :: [[a]] -> [[a]]
on transpose(xss)
    script column
        on |λ|(_, iCol)
            script row
                on |λ|(xs)
                    item iCol of xs
                end |λ|
            end script

            map(row, xss)
        end |λ|
    end script

    map(column, item 1 of xss)
end transpose

-- [Text] -> Text
on unlines(lstLines)
    intercalate(linefeed, lstLines)
end unlines

-- [Text] -> Text
on unwords(lstWords)
    intercalate(" ", lstWords)
end unwords

-- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
on zipWith(f, xs, ys)
    set lng to min(length of xs, length of ys)
    set lst to {}
    tell mReturn(f)
        repeat with i from 1 to lng
            set end of lst to |λ|(item i of xs, item i of ys)
        end repeat
        return lst
    end tell
end zipWith


/* ARM assembly Raspberry PI  */
/*  program alignColumn.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"
.equ BUFFERSIZE, 20 * 10

/*********************************/
/* Initialized data              */
/*********************************/
.data
szMessLeft:        .asciz "LEFT :\n"
szMessRight:       .asciz "\nRIGHT :\n"
szMessCenter:      .asciz "\nCENTER :\n"
szCarriageReturn:  .asciz "\n"

szLine1:    .asciz "Given$a$text$file$of$many$lines,$where$fields$within$a$line$"
szLine2:    .asciz "are$delineated$by$a$single$'dollar'$character,$write$a$program"
szLine3:    .asciz "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$"
szLine4:    .asciz "column$are$separated$by$at$least$one$space."
szLine5:    .asciz "Further,$allow$for$each$word$in$a$column$to$be$either$left$"
szLine6:    .asciz "justified,$right$justified,$or$center$justified$within$its$column."

itbPtLine:    .int szLine1,szLine2,szLine3,szLine4,szLine5,szLine6
              .equ NBLINES, (. - itbPtLine) / 4

/*********************************/
/* UnInitialized data            */
/*********************************/
.bss
/*********************************/
/*  code section                 */
/*********************************/
.text
.global main
main:                               @ entry of program
    ldr r0,iAdritbPtLine
    bl computeMaxiLengthWords
    mov r10,r0                      @ column counter
    ldr r0,iAdrszMessLeft
    bl affichageMess
    ldr r0,iAdritbPtLine
    mov r1,r10                      @ column size
    bl alignLeft
    ldr r0,iAdrszMessRight
    bl affichageMess
    ldr r0,iAdritbPtLine
    mov r1,r10                      @ column size
    bl alignRight
    ldr r0,iAdrszMessCenter
    bl affichageMess
    ldr r0,iAdritbPtLine
    mov r1,r10                      @ column size
    bl alignCenter
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
iAdrszMessLeft:           .int szMessLeft
iAdrszMessRight:          .int szMessRight
iAdrszMessCenter:         .int szMessCenter
iAdritbPtLine:            .int itbPtLine
/******************************************************************/
/*      compute maxi words                                 */
/******************************************************************/
/* r0 contains adresse pointer array */
computeMaxiLengthWords:
    push {r1-r6,lr}          @ save registers
    mov r2,#0                @ indice pointer array
    mov r3,#0                @ maxi length words
1:
    ldr r1,[r0,r2,lsl #2]    @ load pointer
    mov r4,#0                @ length words counter
    mov r5,#0                @ indice line character
2:
    ldrb r6,[r1,r5]          @ load a line character
    cmp r6,#0                @ line end ?
    beq 4f
    cmp r6,#'$'              @ separator ?
    bne 3f
    cmp r4,r3
    movgt r3,r4              @ ig greather replace maxi
    mov r4,#-1               @ raz length counter
3:
    add r4,r4,#1
    add r5,r5,#1             @ increment character indice
    b 2b                     @ and loop
4:                           @ end line
    cmp r4,r3                @ compare word counter and maxi
    movgt r3,r4              @ if greather replace maxi
    add r2,r2,#1             @ increment indice line pointer
    cmp r2,#NBLINES          @ maxi ?
    blt 1b                   @ no -> loop

    mov r0,r3                @ return maxi length counter
100:
    pop {r1-r6,pc}

/******************************************************************/
/*      align left                                                */
/******************************************************************/
/* r0 contains the address of pointer array*/
/* r1 contains column size */
alignLeft:
    push {r4-r7,fp,lr}       @ save registers
    sub sp,sp,#BUFFERSIZE    @ reserve place for output buffer
    mov fp,sp
    mov r5,r0                @ array address
    mov r2,#0                @ indice array
1:
    ldr r3,[r5,r2,lsl #2]    @ load line pointer
    mov r4,#0                @ line character indice
    mov r7,#0                @ output buffer character indice
    mov r6,#0                @ word lenght
2:
    ldrb r0,[r3,r4]          @ load a character line
    strb r0,[fp,r7]          @ store in buffer
    cmp r0,#0                @ line end ?
    beq 6f
    cmp r0,#'$'              @ separator ?
    bne 5f
    mov r0,#' '
    strb r0,[fp,r7]          @ replace $ by space
3:
    cmp r6,r1                @ length word >= length column
    bge 4f
    add r7,r7,#1
    mov r0,#' '
    strb r0,[fp,r7]          @ add space to buffer
    add r6,r6,#1
    b 3b                     @ and loop
4:
    mov r6,#-1               @ raz word length
5:
    add r4,r4,#1             @ increment line indice
    add r7,r7,#1             @ increment buffer indice
    add r6,r6,#1             @ increment word length
    b 2b

6:
    mov r0,#'\n'
    strb r0,[fp,r7]          @ return line
    add r7,r7,#1
    mov r0,#0
    strb r0,[fp,r7]          @ final zéro
    mov r0,fp
    bl affichageMess         @ display output buffer
    add r2,r2,#1
    cmp r2,#NBLINES
    blt 1b

100:
    add sp,sp,#BUFFERSIZE
    pop {r4-r7,fp,pc}
/******************************************************************/
/*      align right                                                */
/******************************************************************/
/* r0 contains the address of pointer array*/
/* r1 contains column size */
alignRight:
    push {r4-r9,fp,lr}       @ save registers
    sub sp,sp,#BUFFERSIZE    @ reserve place for output buffer
    mov fp,sp
    mov r5,r0                @ array address
    mov r2,#0                @ indice array
1:
    ldr r3,[r5,r2,lsl #2]    @ load line pointer
    mov r4,#0                @ line character indice
    mov r7,#0                @ output buffer character indice
    mov r6,#0                @ word lenght
    mov r8,r3                @ word begin address
2:                           @ compute word length
    ldrb r0,[r3,r4]          @ load a character line
    cmp r0,#0                @ line end ?
    beq 3f
    cmp r0,#'$'              @ separator ?
    beq 3f
    add r4,r4,#1             @ increment line indice
    add r6,r6,#1             @ increment word length
    b 2b

3:
    cmp r6,#0
    beq 4f
    sub r6,r1,r6             @ compute left spaces to add
4:                           @ loop add spaces to buffer
    cmp r6,#0
    blt 5f
    mov r0,#' '
    strb r0,[fp,r7]          @ add space to buffer
    add r7,r7,#1
    sub r6,r6,#1
    b 4b                     @ and loop
5:
    mov r9,#0
6:                           @ copy loop word to buffer
    ldrb r0,[r8,r9]
    cmp r0,#'$'
    beq 7f
    cmp r0,#0                @ line end
    beq 8f
    strb r0,[fp,r7]
    add r7,r7,#1
    add r9,r9,#1
    b 6b
7:
    add r8,r8,r9
    add r8,r8,#1             @ new word begin
    mov r6,#0                @ raz word length
    add r4,r4,#1             @ increment line indice
    b 2b

8:
    mov r0,#'\n'
    strb r0,[fp,r7]          @ return line
    add r7,r7,#1
    mov r0,#0
    strb r0,[fp,r7]          @ final zéro
    mov r0,fp
    bl affichageMess         @ display output buffer
    add r2,r2,#1
    cmp r2,#NBLINES
    blt 1b

100:
    add sp,sp,#BUFFERSIZE
    pop {r4-r9,fp,pc}
/******************************************************************/
/*      align center                                                */
/******************************************************************/
/* r0 contains the address of pointer array*/
/* r1 contains column size */
alignCenter:
    push {r4-r12,lr}         @ save registers
    sub sp,sp,#BUFFERSIZE    @ reserve place for output buffer
    mov fp,sp
    mov r5,r0                @ array address
    mov r2,#0                @ indice array
1:
    ldr r3,[r5,r2,lsl #2]    @ load line pointer
    mov r4,#0                @ line character indice
    mov r7,#0                @ output buffer character indice
    mov r6,#0                @ word length
    mov r8,r3                @ word begin address
2:                           @ compute word length
    ldrb r0,[r3,r4]          @ load a character line
    cmp r0,#0                @ line end ?
    beq 3f
    cmp r0,#'$'              @ separator ?
    beq 3f
    add r4,r4,#1             @ increment line indice
    add r6,r6,#1             @ increment word length
    b 2b
3:
    cmp r6,#0
    beq 5f
    sub r6,r1,r6             @ total spaces number to add
    mov r12,r6
    lsr r6,r6,#1             @ divise by 2 = left spaces number
4:
    cmp r6,#0
    blt 5f
    mov r0,#' '
    strb r0,[fp,r7]          @ add space to buffer
    add r7,r7,#1             @ increment output indice
    sub r6,r6,#1             @ decrement number space
    b 4b                     @ and loop
5:
    mov r9,#0
6:                           @ copy loop word to buffer
    ldrb r0,[r8,r9]
    cmp r0,#'$'              @ séparator ?
    beq 7f
    cmp r0,#0                @ line end ?
    beq 10f
    strb r0,[fp,r7]
    add r7,r7,#1
    add r9,r9,#1
    b 6b
7:
    lsr r6,r12,#1            @ divise total spaces by 2
    sub r6,r12,r6            @ and compute number spaces to right side
8:                           @ loop to add right spaces
    cmp r6,#0
    ble 9f
    mov r0,#' '
    strb r0,[fp,r7]          @ add space to buffer
    add r7,r7,#1
    sub r6,r6,#1
    b 8b                     @ and loop

9:
    add r8,r8,r9
    add r8,r8,#1             @ new address word begin
    mov r6,#0                @ raz word length
    add r4,r4,#1             @ increment line indice
    b 2b                     @ and loop new word

10:
    mov r0,#'\n'
    strb r0,[fp,r7]          @ return line
    add r7,r7,#1
    mov r0,#0
    strb r0,[fp,r7]          @ final zéro
    mov r0,fp
    bl affichageMess         @ display output buffer
    add r2,r2,#1             @ increment line indice
    cmp r2,#NBLINES          @ maxi ?
    blt 1b                   @ loop

100:
    add sp,sp,#BUFFERSIZE
    pop {r4-r12,pc}
/***************************************************/
/*      ROUTINES INCLUDE                           */
/***************************************************/
.include "../affichage.inc"


text: {
    Given$a$text$file$of$many$lines,$where$fields$within$a$line$
    are$delineated$by$a$single$'dollar'$character,$write$a$program
    that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$
    column$are$separated$by$at$least$one$space.
    Further,$allow$for$each$word$in$a$column$to$be$either$left$
    justified,$right$justified,$or$center$justified$within$its$column.
}

output: map split.lines text => [split.by:"$"]

loop output 'line [
    loop line 'word -> prints pad word 12
    print ""
]


Alignment := "L"																				; Options: L, R, C
Text =
( LTrim
	Given$a$text$file$of$many$lines,$where$fields$within$a$line$
	are$delineated$by$a$single$'dollar'$character,$write$a$program
	that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$
	column$are$separated$by$at$least$one$space.
	Further,$allow$for$each$word$in$a$column$to$be$either$left$
	justified,$right$justified,$or$center$justified$within$its$column.
)

Loop, Parse, Text																				; calculate column's width
	If A_LoopField in $,`n
		If (N > W)
			W := N, N := 0
		Else
			N := 0
	Else
		++N
Width := ++W

Loop, Parse, Text, `n																			; process each line
{
	Words := StrSplit(A_LoopField, "$")
	For i, Word in Words																		; process each word
		Line .= Align(Word, Alignment, Width)
	Result .= RTrim(Line) . "`n"
	Line := ""
}

Clipboard := Result																				; present results
MsgBox, The results are in the Clipboard

Align(Pal, How, Width) {																		; function for alignment
	Length := StrLen(Pal)
	If (How = "L")
		Return Pal . Spc(Width - Length)
	Else If (How = "R")
		Return Spc(Width - Length) . Pal
	Else If (How = "C")
		Return Spc((Width - Length)//2) . Pal . Spc(Width - Length - (Width - Length)//2)
}

Spc(Number) {																					; function to concatenate space characters
	Loop, %Number%
		Ret .= A_Space
	Return Ret
}


; == If the given text is in an file, it will read with:
#include <File.au3>
Global $aRead
_FileReadToArray($sPath, $aRead)  ; == $aRead[0] includes count of lines, every line stored in one item (without linebreak)

; == For example we get the same result with StringSplit()
Global $sText = _
"Given$a$text$file$of$many$lines,$where$fields$within$a$line$" & @CRLF & _
"are$delineated$by$a$single$'dollar'$character,$write$a$program" & @CRLF & _
"that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$" & @CRLF & _
"column$are$separated$by$at$least$one$space." & @CRLF & _
"Further,$allow$for$each$word$in$a$column$to$be$either$left$" & @CRLF & _
"justified,$right$justified,$or$center$justified$within$its$column." & @CRLF

$aRead = StringSplit($sText, @CRLF, 1)

; == strip leading and trailing "$" and trailing spaces, count remaining "$" to get max column number
Global $iMaxColumn = 0, $iLines = 0
For $i = 1 To $aRead[0]
	If $aRead[$i] = '' Then ContinueLoop   ; skip empty lines
	$iLines += 1
	$aRead[$i] = StringRegExpReplace(StringRegExpReplace(StringRegExpReplace($aRead[$i], '^\$', ''), '\$$', ''), '\s*$', '')
	StringReplace($aRead[$i], '$', '$')
	If @extended +1 > $iMaxColumn Then $iMaxColumn = @extended +1
Next

; == build array to store all fields and length of every item
Global $aFields[$iLines][$iMaxColumn +1][2]
; == and store the max. length of item in columns
Global $aColLen[$iMaxColumn]

; == fill the array
Global $aSplitLine
$iLines = 0
For $i = 1 To $aRead[0]
	If $aRead[$i] = '' Then ContinueLoop   ; skip empty lines
	$iMaxColLen = 0
	$aSplitLine = StringSplit($aRead[$i], '$')
	For $j = 1 To $aSplitLine[0]
		$aFields[$iLines][$j-1][0] = $aSplitLine[$j]
		$aFields[$iLines][$j-1][1] = StringLen($aSplitLine[$j])
		If $aFields[$iLines][$j-1][1] > $aColLen[$j-1] Then $aColLen[$j-1] = $aFields[$iLines][$j-1][1]
	Next
	$iLines += 1
Next

; == let the user select the alignment for every column
$sAlign = InputBox('Column alignment', 'There are ' & $iMaxColumn & ' columns.' & @LF & '0 = left    1 = center    2 = right' & @LF &  _
                   'Input alignment for all columns without delimiters.' & @LF & 'Let it empty, to align all left.')
If $sAlign = '' Then
	For $i = 1 To $iMaxColumn
		$sAlign &= '0'
	Next
EndIf
Global $aAlignment = StringSplit($sAlign, '', 2)

; == output all to console
Global $sLineOut
For $i = 0 To UBound($aFields) -1
	$sLineOut = ''
	For $j = 0 To $iMaxColumn -1
		If $aFields[$i][$j][0] = '' Then ContinueLoop
		$sLineOut &= _GetAligned($aFields[$i][$j][0], $aFields[$i][$j][1], $aAlignment[$j], $aColLen[$j])
	Next
	ConsoleWrite(StringTrimRight($sLineOut, 1) & @LF)
Next

Func _GetAligned($_sString, $_iLen, $_iAlign, $_iMaxLen)
	Local $sSpace = ''
	For $i = 1 To $_iMaxLen
		$sSpace &= ' '
	Next
	Switch $_iAlign
		Case 0
			Return $_sString & StringLeft($sSpace, $_iMaxLen - $_iLen +1)
		Case 1
			Local $iLenLeft = Int(($_iMaxLen - $_iLen)/2)
			Local $iLenRight = $_iMaxLen - $iLenLeft - $_iLen
			Return StringLeft($sSpace, $iLenLeft) & $_sString & StringLeft($sSpace, $iLenRight) & ' '
		Case 2
			Return StringLeft($sSpace, $_iMaxLen - $_iLen) & $_sString & ' '
	EndSwitch
EndFunc  ;==>_GetAligned


# syntax: GAWK -f ALIGN_COLUMNS.AWK ALIGN_COLUMNS.TXT
BEGIN {
    colsep = " " # separator between columns
    report("raw data")
}
{   printf("%s\n",$0)
    arr[NR] = $0
    n = split($0,tmp_arr,"$")
    for (j=1; j<=n; j++) {
      width = max(width,length(tmp_arr[j]))
    }
}
END {
    report("left justified")
    report("right justified")
    report("center justified")
    exit(0)
}
function report(text,  diff,i,j,l,n,r,tmp_arr) {
    printf("\nreport: %s\n",text)
    for (i=1; i<=NR; i++) {
      n = split(arr[i],tmp_arr,"$")
      if (tmp_arr[n] == "") { n-- }
      for (j=1; j<=n; j++) {
        if (text ~ /^[Ll]/) { # left
          printf("%-*s%s",width,tmp_arr[j],colsep)
        }
        else if (text ~ /^[Rr]/) { # right
          printf("%*s%s",width,tmp_arr[j],colsep)
        }
        else if (text ~ /^[Cc]/) { # center
          diff = width - length(tmp_arr[j])
          l = r = int(diff / 2)
          if (diff != l + r) { r++ }
          printf("%*s%s%*s%s",l,"",tmp_arr[j],r,"",colsep)
        }
      }
      printf("\n")
    }
}
function max(x,y) { return((x > y) ? x : y) }


DECLARE in$[] = { "Given$a$text$file$of$many$lines,$where$fields$within$a$line$", \
                  "are$delineated$by$a$single$'dollar'$character,$write$a$program", \
                  "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$", \
                  "column$are$separated$by$at$least$one$space.", \
                  "Further,$allow$for$each$word$in$a$column$to$be$either$left$", \
                  "justified,$right$justified,$or$center$justified$within$its$column." }

OPTION DELIM "$"

CONST items = 6

SUB Print_In_Columns(style)

    ' Find widest column
    FOR y = 0 TO items-1
        FOR x = 1 TO AMOUNT(in$[y])
            IF LEN(TOKEN$(in$[y], x)) > max THEN max = LEN(TOKEN$(in$[y], x))
        NEXT
    NEXT

    ' Print aligned
    FOR y = 0 TO items-1
        FOR x = 1 TO AMOUNT(in$[y])
            PRINT ALIGN$(TOKEN$(in$[y], x), max+1, style);
        NEXT
        PRINT
    NEXT
    PRINT

END SUB

Print_In_Columns(0)
Print_In_Columns(1)
Print_In_Columns(2)


      DATA 6
      DATA "Given$a$text$file$of$many$lines,$where$fields$within$a$line$"
      DATA "are$delineated$by$a$single$'dollar'$character,$write$a$program"
      DATA "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$"
      DATA "column$are$separated$by$at$least$one$space."
      DATA "Further,$allow$for$each$word$in$a$column$to$be$either$left$"
      DATA "justified,$right$justified,$or$center$justified$within$its$column."

      REM First find the maximum length of a 'word':
      max% = 0
      READ nlines%
      FOR Line% = 1 TO nlines%
        READ text$
        REPEAT
          word$ = FNword(text$, "$")
          IF LEN(word$) > max% THEN max% = LEN(word$)
        UNTIL word$ = ""
      NEXT Line%
      @% = max% : REM set column width

      REM Now display the aligned text:
      RESTORE
      READ nlines%
      FOR Line% = 1 TO nlines%
        READ text$
        REPEAT
          word$ = FNword(text$, "$")
          PRINT FNjustify(word$, max%, "left"),;
        UNTIL word$ = ""
        PRINT
      NEXT Line%

      END

      DEF FNword(text$, delim$)
      PRIVATE delim%
      LOCAL previous%
      IF delim% = 0 THEN
        previous% = 1
      ELSE
        previous% = delim% + LEN(delim$)
      ENDIF
      delim% = INSTR(text$+delim$, delim$, previous%)
      IF delim% = 0 THEN
        = ""
      ELSE
        = MID$(text$, previous%, delim%-previous%) + " "
      ENDIF

      DEF FNjustify(word$, field%, mode$)
      IF word$ = "" THEN = ""
      CASE mode$ OF
        WHEN "center": = STRING$((field%-LEN(word$)) DIV 2, " ") + word$
        WHEN "right": = STRING$(field%-LEN(word$), " ") + word$
      ENDCASE
      = word$


@echo off
setlocal enabledelayedexpansion
mode con cols=103

echo Given$a$text$file$of$many$lines,$where$fields$within$a$line$ >file.txt
echo are$delineated$by$a$single$'dollar'$character,$write$a$program! >>file.txt
echo that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$>>file.txt
echo column$are$separated$by$at$least$one$space.>>file.txt
echo Further,$allow$for$each$word$in$a$column$to$be$either$left$>>file.txt
echo justified,$right$justified,$or$center$justified$within$its$column.>>file.txt

for /f "tokens=1-13 delims=$" %%a in ('type file.txt') do (
 call:maxlen %%a %%b %%c %%d %%e %%f %%g %%h %%i %%j %%k %%l %%m  )
echo.
for /f "tokens=1-13 delims=$" %%a in ('type file.txt') do (
 call:align 1 %%a %%b %%c %%d %%e %%f %%g %%h %%i %%j %%k %%l %%m  )
echo.
for /f "tokens=1-13 delims=$" %%a in ('type file.txt') do (
 call:align 2 %%a %%b %%c %%d %%e %%f %%g %%h %%i %%j %%k %%l %%m  )
echo.
for /f "tokens=1-13 delims=$" %%a in ('type file.txt') do (
 call:align 3 %%a %%b %%c %%d %%e %%f %%g %%h %%i %%j %%k %%l %%m  )

exit /B

:maxlen     &::sets variables len1 to len13
  set "cnt=1"
:loop1
  if "%1"=="" exit /b
  call:strlen %1 length
  if !len%cnt%! lss !length! set len%cnt%=!length!
  set /a cnt+=1
  shift
  goto loop1

:align
  setlocal
  set cnt=1
  set print=
:loop2
  if "%2"=="" echo(%print%&endlocal & exit /b
  set /a width=len%cnt%,cnt+=1
  set arr=%2
  if %1 equ 1 call:left   %width% arr
  if %1 equ 2 call:right  %width% arr
  if %1 equ 3 call:center %width% arr
  set "print=%print%%arr% "
  shift /2
  goto loop2

:left %num% &string
  setlocal
   set "arr=!%2!                     "
   set arr=!arr:~0,%1!
  endlocal & set %2=%arr%
exit /b

:right %num% &string
  setlocal
   set "arr=                    !%2!"
   set arr=!arr:~-%1!
   endlocal & set %2=%arr%
exit /b

:center %num% &string
setlocal
  set /a width=%1-1
  set arr=!%2!
  :loop3
  if "!arr:~%width%,1!"=="" set "arr=%arr% "
  if "!arr:~%width%,1!"=="" set "arr= %arr%"
  if "!arr:~%width%,1!"=="" goto loop3
endlocal & set %2=%arr%
exit /b

:strlen  StrVar  &RtnVar
  setlocal EnableDelayedExpansion
  set "s=#%~1"
  set "len=0"
  for %%N in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
    if "!s:~%%N,1!" neq "" set /a "len+=%%N" & set "s=!s:~%%N!"
  )
  endlocal & set %~2=%len%
exit /b


beads 1 program 'Align columns'

const
	text = '''Given$a$text$file$of$many$lines,$where$fields$within$a$line$
are$delineated$by$a$single$'dollar'$character,$write$a$program
that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$
column$are$separated$by$at$least$one$space.
Further,$allow$for$each$word$in$a$column$to$be$either$left$
justified,$right$justified,$or$center$justified$within$its$column.'''

var
	words : array^2 of str
	widths : array of num

calc div_line
	var s = '+'
	loop across:widths val:w
		s = s & str_repeat('-',w) & '+'
	log s

calc show_table(
	justify
	)
	loop across:words index:i
		var s = '|'
		loop across:widths index:j val:w
			var
				word = words[i,j]
			if word == U
				word = ''
			case justify
			| RIGHT
				s = s & pad_left(word,w)
			| LEFT
				s = s & pad_right(word,w)
			| CENTER
				w = w - str_len(word)
				s = s & str_repeat(' ',idiv(w,2)) & word & str_repeat(' ',idiv(w,2)+mod(w,2))
			s = s & '|'
		log s
		div_line

calc main_init
	var w
	split_lines_words (text, words, delim:'$')
	loop across:words index:i
		loop across:words[i] index:j
			w = str_len(words[i,j])
			widths[j] = max(w,widths[j])
	loop across:[LEFT CENTER RIGHT] val:v
		log "\n{v} justified\n"
		div_line
		show_table(v)


Split ← (⊢-˜+`×¬)∘=⊔⊢
PadRow ← {
  w‿t𝕊𝕩: # t → type.
         # 0 → left
         # 1 → right
         # 2 → center
 pstyle←t⊑⟨{0‿𝕩},{𝕩‿0},{⟨⌊𝕩÷2,⌈𝕩÷2⟩}⟩
 𝕩{(⊣∾𝕨∾⊢)´(Pstyle 𝕩)/¨<w}¨(⌈´-⊢)≠¨𝕩
}
Align ← {{𝕨∾' '∾𝕩}´˘⍉" "‿𝕨⊸PadRow˘⍉>⟨""⟩‿0 PadRow '$' Split¨(@+10) Split 𝕩}

1 Align text


using System;
class ColumnAlignerProgram
{
    delegate string Justification(string s, int width);

    static string[] AlignColumns(string[] lines, Justification justification)
    {
        const char Separator = '$';
        // build input table and calculate columns count
        string[][] table = new string[lines.Length][];
        int columns = 0;
        for (int i = 0; i < lines.Length; i++)
        {
            string[] row = lines[i].TrimEnd(Separator).Split(Separator);
            if (columns < row.Length) columns = row.Length;
            table[i] = row;
        }
        // create formatted table
        string[][] formattedTable = new string[table.Length][];
        for (int i = 0; i < formattedTable.Length; i++)
        {
            formattedTable[i] = new string[columns];
        }
        for (int j = 0; j < columns; j++)
        {
            // get max column width
            int columnWidth = 0;
            for (int i = 0; i < table.Length; i++)
            {
                if (j < table[i].Length && columnWidth < table[i][j].Length)
                    columnWidth = table[i][j].Length;
            }
            // justify column cells
            for (int i = 0; i < formattedTable.Length; i++)
            {
                if (j < table[i].Length)
                    formattedTable[i][j] = justification(table[i][j], columnWidth);
                else
                    formattedTable[i][j] = new String(' ', columnWidth);
            }
        }
        // create result
        string[] result = new string[formattedTable.Length];
        for (int i = 0; i < result.Length; i++)
        {
            result[i] = String.Join(" ", formattedTable[i]);
        }
        return result;
    }

    static string JustifyLeft(string s, int width) { return s.PadRight(width); }
    static string JustifyRight(string s, int width) { return s.PadLeft(width); }
    static string JustifyCenter(string s, int width)
    {
        return s.PadLeft((width + s.Length) / 2).PadRight(width);
    }

    static void Main()
    {
        string[] input = {
            "Given$a$text$file$of$many$lines,$where$fields$within$a$line$",
            "are$delineated$by$a$single$'dollar'$character,$write$a$program",
            "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$",
            "column$are$separated$by$at$least$one$space.",
            "Further,$allow$for$each$word$in$a$column$to$be$either$left$",
            "justified,$right$justified,$or$center$justified$within$its$column.",
        };

        foreach (string line in AlignColumns(input, JustifyCenter))
        {
            Console.WriteLine(line);
        }
    }
}


(ns rosettacode.align-columns
  (:require [clojure.contrib.string :as str]))

(def data "Given$a$text$file$of$many$lines,$where$fields$within$a$line$
are$delineated$by$a$single$'dollar'$character,$write$a$program
that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$
column$are$separated$by$at$least$one$space.
Further,$allow$for$each$word$in$a$column$to$be$either$left$
justified,$right$justified,$or$center$justified$within$its$column.")

(def table (map #(str/split #"\$" %) (str/split-lines data)))

(defn col-width [n table] (reduce max (map #(try (count (nth % n))
                                               (catch Exception _  0))
                                           table)))
(defn spaces [n] (str/repeat n " "))
(defn add-padding
  "if the string is too big turncate it, else return a string with padding"
  [string width justification]
  (if (>= (count string) width) (str/take width string)
      (let [pad-len (int (- width (count string))) ;we don't want rationals
            half-pad-len (int (/ pad-len 2))]
        (case justification
              :right (str (spaces pad-len) string)
              :left  (str string (spaces pad-len))
              :center (str (spaces half-pad-len) string (spaces (- pad-len half-pad-len)))))))

(defn aligned-table
  "get the width of each column, then generate a new table with propper padding for eath item"
  ([table justification]
  (let [col-widths (map #(+ 2 (col-width % table)) (range (count(first table))))]
    (map
     (fn [row] (map #(add-padding %1 %2 justification) row col-widths))
     table))))

(defn print-table
  [table]
  (do (println)
      (print (str/join "" (flatten (interleave table (repeat "\n")))))))

(print-table (aligned-table table :center))


       identification division.
       program-id. AlignColumns.

       data division.
       working-storage section.
      *>-> Constants
       78 MAX-LINES value 6.
       78 MAX-LINE-SIZE value 66.
       78 MAX-COLUMNS value 12.
       78 MAX-COLUMN-SIZE value 16.
      *>-> Indexes
       01 w-idx                   pic is 9(2).
       01 w-idy                   pic is 9(2).
       01 w-pos                   pic is 9(3).
      *>-> Data structures
       01 w-lines.
          05 w-line               pic is x(MAX-LINE-SIZE) occurs MAX-LINES.
       01 w-column-sizes.
          05 w-column-size        pic is 99 occurs MAX-COLUMNS value zeros.
       01 w-matrix.
          05 filler               occurs MAX-LINES.
             10 filler            occurs MAX-COLUMNS.
                15 w-content      pic is x(MAX-COLUMN-SIZE).
      *>-> Output
       01 w-line-out              pic is x(120).
      *>-> Data alignment
       01 w-alignment             pic is x(1).
          88 alignment-left       value is "L".
          88 alignment-center     value is "C".
          88 alignment-right      value is "R".

       procedure division.
       main.
           move "Given$a$text$file$of$many$lines,$where$fields$within$a$line$" to w-line(1)
           move "are$delineated$by$a$single$'dollar'$character,$write$a$program" to w-line(2)
           move "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$" to w-line(3)
           move "column$are$separated$by$at$least$one$space." to w-line(4)
           move "Further,$allow$for$each$word$in$a$column$to$be$either$left$" to w-line(5)
           move "justified,$right$justified,$or$center$justified$within$its$column." to w-line(6)
           perform calculate-size-columns
           set alignment-left to true
           perform show-content
           set alignment-center to true
           perform show-content
           set alignment-right to true
           perform show-content
           goback
           .
       calculate-size-columns.
           perform
              varying             w-idx from 1 by 1
                 until            w-idx > MAX-LINES
              unstring w-line(w-idx) delimited by "$" into w-content(w-idx, 1), w-content(w-idx, 2),
                  w-content(w-idx, 3), w-content(w-idx, 4), w-content(w-idx, 5), w-content(w-idx, 6),
                  w-content(w-idx, 7), w-content(w-idx, 8), w-content(w-idx, 9), w-content(w-idx, 10),
                  w-content(w-idx, 11), w-content(w-idx, 12),
              perform
                 varying          w-idy from 1 by 1
                    until         w-idy > MAX-COLUMNS
                 if function stored-char-length(w-content(w-idx, w-idy)) > w-column-size(w-idy)
                    move function stored-char-length(w-content(w-idx, w-idy)) to w-column-size(w-idy)
                 end-if
              end-perform
           end-perform
           .
       show-content.
           move all "-" to w-line-out
           display w-line-out
           perform
              varying             w-idx from 1 by 1
                 until            w-idx > MAX-LINES
              move spaces to w-line-out
              move 1 to w-pos
              perform
                 varying          w-idy from 1 by 1
                    until         w-idy > MAX-COLUMNS
                 call "C$JUSTIFY" using w-content(w-idx, w-idy)(1:w-column-size(w-idy)), w-alignment
                 move w-content(w-idx, w-idy) to w-line-out(w-pos:w-column-size(w-idy))
                 compute w-pos = w-pos + w-column-size(w-idy) + 1
              end-perform
              display w-line-out
           end-perform
           .


pad = (n) ->
  s = ''
  while n > 0
    s += ' '
    n -= 1
  s

align = (input, alignment = 'center') ->
  tokenized_lines = (line.split '$' for line in input)
  col_widths = {}
  for line in tokenized_lines
    for token, i in line
      if !col_widths[i]? or token.length > col_widths[i]
        col_widths[i] = token.length
  padders =
    center: (s, width) ->
      excess = width - s.length
      left = Math.floor excess / 2
      right = excess - left
      pad(left) + s + pad(right)

    right: (s, width) ->
      excess = width - s.length
      pad(excess) + s

    left: (s, width) ->
      excess = width - s.length
      s + pad(excess)

  padder = padders[alignment]

  for line in tokenized_lines
    padded_tokens = (padder(token, col_widths[i]) for token, i in line)
    console.log padded_tokens.join ' '

input = [
  "Given$a$text$file$of$many$lines,$where$fields$within$a$line$"
  "are$delineated$by$a$single$'dollar'$character,$write$a$program"
  "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$"
  "column$are$separated$by$at$least$one$space."
  "Further,$allow$for$each$word$in$a$column$to$be$either$left$"
  "justified,$right$justified,$or$center$justified$within$its$column."
]

for alignment in ['center', 'right', 'left']
  console.log "\n----- #{alignment}"
  align input, alignment


(defun nonempty (seq)
  (position-if (lambda (x) (declare (ignore x)) t) seq))

(defun split (delim seq)
  "Splits seq on delim into a list of subsequences. Trailing empty
subsequences are removed."
  (labels
      ((f (seq &aux (pos (position delim seq)))
         (if pos
             (cons
              (subseq seq 0 pos)
              (f (subseq seq (1+ pos))))
           (list seq))))
    (let* ((list (f seq))
           (end (position-if #'nonempty list :from-end t)))
      (subseq list 0 (1+ end)))))

(defun lengthen (list minlen filler-elem &aux (len (length list)))
  "Destructively pads list with filler-elem up to minlen."
  (if (< len minlen)
      (nconc list (make-list (- minlen len) :initial-element filler-elem))
    list))

(defun align-columns (text
                      &key (align :left)
                      &aux
                      (fmtmod (case align
                                (:left "@")
                                (:right ":")
                                (:center "@:")
                                (t (error "Invalid alignment."))))
                      (fields (mapcar (lambda (line) (split #\$ line))
                                      (split #\Newline text)))
                      (mostcols (loop for l in fields
                                      maximize (length l)))
                      widest)
  (setf fields (mapcar (lambda (l) (lengthen l mostcols ""))
                       fields))
  (setf widest (loop for col below (length (first fields))
                     collect (loop for row in fields
                                   maximize (length (elt row col)))))
  (format nil
          (with-output-to-string (s)
            (princ "~{~{" s)
            (dolist (w widest)
              (format s "~~~d~a<~~a~~>" (1+ w) fmtmod))
            (princ "~}~%~}" s))
          fields))


void main() {
    import std.stdio, std.string, std.algorithm, std.range, std.typetuple;

    immutable data =
"Given$a$txt$file$of$many$lines,$where$fields$within$a$line$
are$delineated$by$a$single$'dollar'$character,$write$a$program
that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$
column$are$separated$by$at$least$one$space.
Further,$allow$for$each$word$in$a$column$to$be$either$left$
justified,$right$justified,$or$center$justified$within$its$column."
    .split.map!(r => r.chomp("$").split("$")).array;

    size_t[size_t] maxWidths;
    foreach (const line; data)
        foreach (immutable i, const word; line)
            maxWidths[i] = max(maxWidths.get(i, 0), word.length);

    foreach (immutable just; TypeTuple!(leftJustify, center, rightJustify))
        foreach (immutable line; data)
            writefln("%-(%s %)", line.length.iota
                     .map!(i => just(line[i], maxWidths[i], ' ')));
}


USES
   StdCtrls, Classes, SysUtils, StrUtils, Contnrs;

procedure AlignByColumn(Output: TMemo; Align: TAlignment);
const
   TextToAlign =
   'Given$a$text$file$of$many$lines,$where$fields$within$a$line$'#$D#$A +
   'are$delineated$by$a$single$''dollar''$character,$write$a$program'#$D#$A +
   'that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$'#$D#$A +
   'column$are$separated$by$at$least$one$space.'#$D#$A +
   'Further,$allow$for$each$word$in$a$column$to$be$either$left$'#$D#$A +
   'justified,$right$justified,$or$center$justified$within$its$column.';
var
   TextLine, TempTString: TStringlist;
   TextLines: TObjectList;
   MaxLength, i, j: Byte;
   OutPutString, EmptyString, Item: String;
begin
   TRY
      MaxLength := 0;
      TextLines := TObjectList.Create(True);
      TextLine := TStringList.Create;
      TextLine.text := TextToAlign;
      for i:= 0 to TextLine.Count - 1 do
      begin
         TempTString := TStringlist.create;
         TempTString.text :=AnsiReplaceStr(TextLine[i], '$', #$D#$A);
         TextLines.Add(TempTString);
      end;
      for i := 0 to TextLines.Count - 1 do
         for j := 0 to TStringList(TextLines.Items[i]).Count - 1 do
            If Length(TStringList(TextLines.Items[i])[j]) > MaxLength then
               MaxLength := Length(TStringList(TextLines.Items[i])[j]);
      If MaxLength > 0 then
         MaxLength := MaxLength + 2; // Add to empty spaces to it
      for i := 0 to TextLines.Count - 1 do
      begin
         OutPutString := '';
         for j := 0 to TStringList(TextLines.Items[i]).Count - 1 do
         begin
            EmptyString := StringOfChar(' ', MaxLength);
            Item := TStringList(TextLines.Items[i])[j];
            case Align of
               taLeftJustify: Move(Item[1], EmptyString[2], Length(Item));
               taRightJustify: Move(Item[1], EmptyString[MaxLength - Length(Item) + 1], Length(Item));
               taCenter: Move(Item[1], EmptyString[(MaxLength - Length(Item) + 1) div 2 + 1], Length(Item));
            end;
            OutPutString := OutPutString + EmptyString;
         end;
         Output.Lines.Add(OutPutString);
      end;
   FINALLY
      FreeAndNil(TextLine);
      FreeAndNil(TextLines);
   END;
end;


pragma.enable("accumulator")

def left(width, word) {
  return word + " " * (width - word.size())
}

def center(width, word) {
  def leftCount := (width - word.size()) // 2
  return " " * leftCount + word + " " * (width - word.size() - leftCount)
}

def right(width, word) {
  return " " * (width - word.size()) + word
}

def alignColumns(align, text) {
    def split := accum [] for line in text.split("\n") { _.with(line.split("$")) }
    var widths := []
    for line in split {
      for i => word in line {
        widths with= (i, widths.fetch(i, fn{0}).max(word.size()))
      }
    }
    return accum "" for line in split {
      _ + accum "" for i => word in line {
        _ + align(widths[i] + 1, word)
      } + "\n"
    }
}


defmodule Align do
  def columns(text, alignment) do
    fieldsbyrow = String.split(text, "\n", trim: true)
                  |> Enum.map(fn row -> String.split(row, "$", trim: true) end)
    maxfields = Enum.map(fieldsbyrow, fn field -> length(field) end) |> Enum.max
    colwidths = Enum.map(fieldsbyrow, fn field -> field ++ List.duplicate("", maxfields - length(field)) end)
                |> List.zip
                |> Enum.map(fn column ->
                     Tuple.to_list(column) |> Enum.map(fn col-> String.length(col) end) |> Enum.max
                   end)
    Enum.each(fieldsbyrow, fn row ->
      Enum.zip(row, colwidths)
      |> Enum.map(fn {field, width} -> adjust(field, width, alignment) end)
      |> Enum.join(" ") |> IO.puts
    end)
  end

  defp adjust(field, width, :Left),  do: String.pad_trailing(field, width)
  defp adjust(field, width, :Right), do: String.pad_leading(field, width)
  defp adjust(field, width, _),      do: :string.centre(String.to_charlist(field), width)
end

text = """
Given$a$text$file$of$many$lines,$where$fields$within$a$line$
are$delineated$by$a$single$'dollar'$character,$write$a$program
that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$
column$are$separated$by$at$least$one$space.
Further,$allow$for$each$word$in$a$column$to$be$either$left$
justified,$right$justified,$or$center$justified$within$its$column.
"""

Enum.each([:Left, :Right, :Center], fn alignment ->
  IO.puts "\n# #{alignment} Column-aligned output:"
  Align.columns(text, alignment)
end)


-module (align_columns).

-export([align_left/0, align_right/0, align_center/0]).
-define (Lines,
         ["Given\$a\$text\$file\$of\$many\$lines\$where\$fields\$within\$a\$line\$",
          "are\$delineated\$by\$a\$single\$'dollar'\$character,\$write\$a\$program",
          "that\$aligns\$each\$column\$of\$fields\$by\$ensuring\$that\$words\$in\$each\$",
          "column\$are\$separated\$by\$at\$least\$one\$space.",
          "Further,\$allow\$for\$each\$word\$in\$a\$column\$to\$be\$either\$left\$",
          "justified,\$right\$justified,\$or\$center\$justified\$within\$its\$column."].

align_left()-> align_columns(left).
align_right()-> align_columns(right).
align_center()-> align_columns(centre).
align_columns(Alignment) ->
    Words = [ string:tokens(Line, "\$") || Line <- ?Lines ],
    Words_length  = lists:foldl( fun max_length/2, [], Words),
    Result = [prepare_line(Words_line, Words_length, Alignment)
              || Words_line <- Words],

    [ io:fwrite("~s~n", [lists:flatten(Line)]) || Line <- Result],
    ok.

max_length(Words_of_a_line, Acc_maxlength) ->
    Line_lengths = [length(W) || W <- Words_of_a_line ],
    Max_nb_of_length = lists:max([length(Acc_maxlength), length(Line_lengths)]),
    Line_lengths_prepared = adjust_list (Line_lengths, Max_nb_of_length, 0),
    Acc_maxlength_prepared = adjust_list(Acc_maxlength, Max_nb_of_length, 0),
    Two_lengths =lists:zip(Line_lengths_prepared, Acc_maxlength_prepared),
    [ lists:max([A, B]) || {A, B} <- Two_lengths].
adjust_list(L, Desired_length, Elem) ->
    L++lists:duplicate(Desired_length - length(L), Elem).

prepare_line(Words_line, Words_length, Alignment) ->
    All_words = adjust_list(Words_line, length(Words_length), ""),
    Zipped = lists:zip (All_words, Words_length),
    [ apply(string, Alignment, [Word, Length + 1, $\s])
      || {Word, Length} <- Zipped].


constant data = {
    "Given$a$text$file$of$many$lines,$where$fields$within$a$line$",
    "are$delineated$by$a$single$'dollar'$character,$write$a$program",
    "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$",
    "column$are$separated$by$at$least$one$space.",
    "Further,$allow$for$each$word$in$a$column$to$be$either$left$",
    "justified,$right$justified,$or$center$justified$within$its$column."
}

function split(sequence s, integer c)
    sequence out
    integer first, delim
    out = {}
    first = 1
    while first<=length(s) do
        delim = find_from(c,s,first)
        if delim = 0 then
            delim = length(s)+1
        end if
        out = append(out,s[first..delim-1])
        first = delim + 1
    end while
    return out
end function

function align(sequence s, integer width, integer alignment)
    integer n
    n = width - length(s)
    if n <= 0 then
        return s
    elsif alignment < 0 then
        return s & repeat(' ', n)
    elsif alignment > 0 then
        return repeat(' ', n) & s
    else
        return repeat(' ', floor(n/2)) & s & repeat(' ', floor(n/2+0.5))
    end if
end function

integer maxlen
sequence lines
maxlen = 0
lines = repeat(0,length(data))
for i = 1 to length(data) do
    lines[i] = split(data[i],'$')
    for j = 1 to length(lines[i]) do
        if length(lines[i][j]) > maxlen then
            maxlen = length(lines[i][j])
        end if
    end for
end for

for a = -1 to 1 do
    for i = 1 to length(lines) do
        for j = 1 to length(lines[i]) do
            puts(1, align(lines[i][j],maxlen,a) & ' ')
        end for
        puts(1,'\n')
    end for
    puts(1,'\n')
end for


open System
open System.IO

let tableFromPath path =
    let lines =
        [ for line in File.ReadAllLines(path) -> (line.TrimEnd('$').Split('$')) ]
    let width = List.fold (fun max (line : string[]) -> if max < line.Length then line.Length else max) 0 lines
    List.map (fun (a : string[]) -> (List.init width (fun i -> if i < a.Length then a.[i] else ""))) lines

let rec trans m =
    match m with
    | []::_ -> []
    | _ -> (List.map List.head m) :: trans (List.map List.tail m)

let colWidth table =
    List.map (fun col -> List.max (List.map String.length col)) (trans table)

let left = (fun (s : string) n -> s.PadRight(n))
let right = (fun (s : string) n -> s.PadLeft(n))
let center = (fun (s : string) n -> s.PadLeft((n + s.Length) / 2).PadRight(n))

[<EntryPoint>]
let main argv =
    let table = tableFromPath argv.[0]
    let width = Array.ofList (colWidth table)
    let format table align =
        List.map (fun (row : string list) -> List.mapi (fun i s -> sprintf "%s" (align s width.[i])) row) table
        |> List.iter (fun row -> printfn "%s" (String.Join(" ", Array.ofList row)))

    for align in [ left; right; center ] do
        format table align
        printfn "%s" (new String('-', (Array.sum width) + width.Length - 1))
    0


USING: fry io kernel math math.functions math.order sequences
splitting strings ;
IN: rosetta.column-aligner

CONSTANT: example-text "Given$a$text$file$of$many$lines,$where$fields$within$a$line$
are$delineated$by$a$single$'dollar'$character,$write$a$program
that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$
column$are$separated$by$at$least$one$space.
Further,$allow$for$each$word$in$a$column$to$be$either$left$
justified,$right$justified,$or$center$justified$within$its$column."

: split-and-pad ( text -- lines )
    "\n" split [ "$" split harvest ] map
    dup [ length ] [ max ] map-reduce
    '[ _ "" pad-tail ] map ;

: column-widths ( columns -- widths )
    [ [ length ] [ max ] map-reduce ] map ;

SINGLETONS: +left+ +middle+ +right+ ;

GENERIC: align-string ( str n alignment -- str' )

M: +left+ align-string  drop CHAR: space pad-tail ;
M: +right+ align-string drop CHAR: space pad-head ;

M: +middle+ align-string
    drop
    over length - 2 /
    [ floor CHAR: space <string> ]
    [ ceiling CHAR: space <string> ] bi surround ;

: align-columns ( columns alignment -- columns' )
    [ dup column-widths ] dip '[
        [ _ align-string ] curry map
    ] 2map ;

: print-aligned ( text alignment -- )
    [ split-and-pad flip ] dip align-columns flip
    [ [ write " " write ] each nl ] each ;


#APPTYPE CONSOLE

DIM s =	"Given$a$text$file$of$many$lines,$where$fields$within$a$line$
are$delineated$by$a$single$'dollar'$character,$write$a$program
that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$
column$are$separated$by$at$least$one$space.
Further,$allow$for$each$word$in$a$column$to$be$either$left$
justified,$right$justified,$or$center$justified$within$its$column."

DIM lines[] = SPLIT(s, CRLF), tokens[], l, t, length, margin, justify = "center"

FOREACH l IN lines
	tokens = SPLIT(l, "$")
	FOREACH t IN tokens
		IF STRLEN(t) > length THEN length = INCR(STRLEN)
	NEXT
NEXT

FOREACH l IN lines
	tokens = SPLIT(l, "$")
	FOREACH t IN tokens
		SELECT CASE justify
			CASE "left"
				PRINT t, SPACE(length - STRLEN(t));
			CASE "center"
				margin = (length - STRLEN(t)) \ 2
				PRINT SPACE(margin), t, SPACE(length - STRLEN - margin);
			CASE "right"
				PRINT SPACE(length - STRLEN(t)), t;
		END SELECT
	NEXT
	PRINT
NEXT

PAUSE