%dw 2.0
output application/json

import some from dw::core::Arrays

fun takeUntil<T>(list: Array<T>, condition: (T, Number) -> Boolean): Array<T> = do {
    fun until(list: Array<T>, condition: (T, Number) -> Boolean, startIndex: Number): Array<T> =
        list match {
            case [] -> []
            case [head ~ tail] ->
                if(condition(head, startIndex))
                    [head ~ until(tail, condition, startIndex + 1)]
                else
                    [head]
        }
    ---
    until(list, condition, 0)
}

fun filter<T>(list: Array<T>, condition: (T, Number) -> Boolean): Array<T> = do {
    fun theFilter(list: Array<T>, condition: (T, Number) -> Boolean, startIndex: Number): Array<T> =
        list match {
            case [] -> []
            case [head ~ tail] ->
                if (condition(head, startIndex))
                    [head ~ theFilter(tail, condition, startIndex + 1)]
                else
                    theFilter(tail, condition, startIndex + 1)
        }
    ---
    theFilter(list, condition, 0)
}

fun map<T, X>(list: Array<T>, transform: (T, Number) -> X): Array<X> = do {
    fun theMap(list: Array<T>, transform: (T, Number) -> X, startIndex: Number): Array<X> =
        list match {
            case [] -> []
            case [head ~ tail] -> [transform(head, startIndex) ~ theMap(tail, transform, startIndex + 1)]
        }
    ---
    theMap(list, transform, 0)
}









fun isPrime(num: Number): Boolean = do {
    // try primes <= 16
    if (num <= 16)
        (num == 2) or (num == 3) or (num == 5) or (num == 7) or (num == 11) or (num == 13)
    else if ( // cull multiples of 2, 3, 5 or 7
           ((num mod 2) == 0)
        or ((num mod 3) == 0)
        or ((num mod 5) == 0)
        or ((num mod 7) == 0)
    )
        false
    // cull square numbers ending in 1, 3, 7 or 9
    else if(
        sequence(1)
            map ($ * 10)
            takeUntil ($ * $) < num
            some (
                    (num mod ($ + 1)) == 0
                or  (num mod ($ + 3)) == 0
                or  (num mod ($ + 7)) == 0
                or  (num mod ($ + 9)) == 0
            )
    )
        false
    else
        true
}

fun sequence(start = 0) = [start ~ sequence(start + 1)]

fun primeGenerator() = (sequence(0) filter isPrime($))

---
primeGenerator()[999 to 888]
