discard """
  targets: "c"
  output: '''
1 3 6 11 20 foo
foo88
23 24foo 88
18
18
99
99
99
99 99
99 99
12 99 99
12 99 99
success
@[1, 2, 5]
click at 10,20
lost focus 1
lost focus 2
registered handler for UserEvent 1
registered handler for UserEvent 2
registered handler for UserEvent 3
registered handler for UserEvent 4
asdas
processClient end
false
baro0
foo88
23 24foo 88
foo88
23 24foo 88
11
@[1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1]
'''
joinable: false
"""


block tclosure:
  proc map(n: var openArray[int], fn: proc (x: int): int {.closure}) =
    for i in 0..n.len-1: n[i] = fn(n[i])

  proc each(n: openArray[int], fn: proc(x: int) {.closure.}) =
    for i in 0..n.len-1:
      fn(n[i])

  var myData: array[0..4, int] = [0, 1, 2, 3, 4]

  proc testA() =
    var p = 0
    map(myData, proc (x: int): int =
                  result = x + 1 shl (proc (y: int): int =
                    return y + p
                  )(0)
                  inc(p))

  testA()

  myData.each do (x: int):
    write(stdout, x)
    write(stdout, " ")

  #OUT 2 4 6 8 10

  # bug #5015

  type Mutator = proc(matched: string): string {.noSideEffect, gcsafe, locks: 0.}

  proc putMutated(
      MutatorCount: static[int],
      mTable: static[array[MutatorCount, Mutator]], input: string) =
    for i in 0..<MutatorCount: echo mTable[i](input)

  proc mutator0(matched: string): string =
      "foo"

  const
    mTable = [Mutator(mutator0)]

  putMutated(1, mTable, "foo")



block tclosure0:
  when true:
    # test simple closure within dummy 'main':
    proc dummy =
      proc main2(param: int) =
        var fooB = 23
        proc outer(outerParam: string) =
          var outerVar = 88
          echo outerParam, outerVar
          proc inner() =
            block Test:
              echo fooB, " ", param, outerParam, " ", outerVar
          inner()
        outer("foo")
      main2(24)

    dummy()

  when true:
    proc outer2(x:int) : proc(y:int):int =   # curry-ed application
        return proc(y:int):int = x*y

    var fn = outer2(6)  # the closure
    echo fn(3)   # it works

    var rawP = fn.rawProc()
    var rawE = fn.rawEnv()

    # A type to cast the function pointer into a nimcall
    type TimesClosure = proc(a: int, x: pointer): int {.nimcall.}

    # Call the function with its closure
    echo cast[TimesClosure](rawP)(3, rawE)

  when true:
    proc outer =
      var x, y: int = 99
      proc innerA = echo x
      proc innerB =
        echo y
        innerA()

      innerA()
      innerB()

    outer()

  when true:
    proc indirectDep =
      var x, y: int = 99
      proc innerA = echo x, " ", y
      proc innerB =
        innerA()

      innerA()
      innerB()

    indirectDep()

  when true:
    proc needlessIndirection =
      var x, y: int = 99
      proc indirection =
        var z = 12
        proc innerA = echo z, " ", x, " ", y
        proc innerB =
          innerA()

        innerA()
        innerB()
      indirection()

    needlessIndirection()






block tclosure3:
  proc main =
    const n = 30
    for iterations in 0..10_000:
      var s: seq[proc(): string {.closure.}] = @[]
      for i in 0 .. n-1:
        (proc () =
          let ii = i
          s.add(proc(): string = return $(ii*ii)))()
      for i in 0 .. n-1:
        let val = s[i]()
        if val != $(i*i): echo "bug  ", val

      if getOccupiedMem() > 5000_000: quit("still a leak!")
    echo "success"

  main()



import json, tables, sequtils
block tclosure4:
  proc run(json_params: OrderedTable) =
    let json_elems = json_params["files"].elems
    # These fail compilation.
    var files = map(json_elems, proc (x: JsonNode): string = x.str)

  let text = """{"files": ["a", "b", "c"]}"""
  run((text.parseJson).fields)



import sugar
block inference3304:
  type
    List[T] = ref object
      val: T

  proc foo[T](l: List[T]): seq[int] =
    @[1,2,3,5].filter(x => x != l.val)

  echo(foo(List[int](val: 3)))



block tcodegenerr1923:
  type
    Foo[M] = proc() : M

  proc bar[M](f : Foo[M]) =
    discard f()

  proc baz() : int = 42

  bar(baz)



block doNotation:
  type
    Button = object
    Event = object
      x, y: int

  proc onClick(x: Button, handler: proc(x: Event)) =
    handler(Event(x: 10, y: 20))

  proc onFocusLost(x: Button, handler: proc()) =
    handler()

  proc onUserEvent(x: Button, eventName: string, handler: proc) =
    echo "registered handler for ", eventName

  var b = Button()

  b.onClick do (e: Event):
    echo "click at ", e.x, ",", e.y

  b.onFocusLost:
    echo "lost focus 1"

  b.onFocusLost do:
    echo "lost focus 2"

  b.onUserEvent("UserEvent 1") do:
    discard

  b.onUserEvent "UserEvent 2":
    discard

  b.onUserEvent("UserEvent 3"):
    discard

  b.onUserEvent("UserEvent 4", () => echo "event 4")



import tables
block fib50:
  proc memoize(f: proc (a: int64): int64): proc (a: int64): int64 =
      var previous = initTable[int64, int64]()
      return proc(i: int64): int64 =
          if not previous.hasKey i:
              previous[i] = f(i)
          return previous[i]

  var fib: proc(a: int64): int64

  fib = memoize(proc (i: int64): int64 =
      if i == 0 or i == 1:
          return 1
      return fib(i-1) + fib(i-2)
  )

  doAssert fib(50) == 20365011074



block tflatmap:
  # bug #3995
  type
    RNG = tuple[]
    Rand[A] = (RNG) -> (A, RNG)

  proc nextInt(r: RNG): (int, RNG) =
    (1, ())

  proc flatMap[A,B](f: Rand[A], g: A -> Rand[B]): Rand[B] =
    (rng: RNG) => (
      let (a, rng2) = f(rng);
      let g1 = g(a);
      g1(rng2)
    )

  proc map[A,B](s: Rand[A], f: A -> B): Rand[B] =
    let g: A -> Rand[B] = (a: A) => ((rng: RNG) => (f(a), rng))
    flatMap(s, g)

  discard nextInt.map(i => i - i mod 2)



block tforum:
  type
    PAsyncHttpServer = ref object
      value: string
    PFutureBase = ref object
      callback: proc () {.closure.}
      value: string
      failed: bool

  proc accept(server: PAsyncHttpServer): PFutureBase =
    new(result)
    result.callback = proc () =
      discard
    server.value = "hahaha"

  proc processClient(): PFutureBase =
    new(result)

  proc serve(server: PAsyncHttpServer): PFutureBase =
    iterator serveIter(): PFutureBase {.closure.} =
      echo server.value
      while true:
        var acceptAddrFut = server.accept()
        yield acceptAddrFut
        var fut = acceptAddrFut.value

        var f = processClient()
        f.callback =
          proc () =
            echo("processClient end")
            echo(f.failed)
        yield f
    var x = serveIter
    for i in 0 .. 1:
      result = x()
      result.callback()

  discard serve(PAsyncHttpServer(value: "asdas"))



block futclosure2138:
  proc any[T](list: varargs[T], pred: (T) -> bool): bool =
    for item in list:
        if pred(item):
            result = true
            break

  proc contains(s: string, words: varargs[string]): bool =
    any(words, (word) => s.contains(word))



block tinterf:
  type
    ITest = tuple[
      setter: proc(v: int) {.closure.},
      getter1: proc(): int {.closure.},
      getter2: proc(): int {.closure.}]

  proc getInterf(): ITest =
    var shared1, shared2: int

    return (setter: proc (x: int) =
              shared1 = x
              shared2 = x + 10,
            getter1: proc (): int = result = shared1,
            getter2: proc (): int = return shared2)

  var i = getInterf()
  i.setter(56)

  doAssert i.getter1() == 56
  doAssert i.getter2() == 66



block tjester:
  type
    Future[T] = ref object
      data: T
      callback: proc () {.closure.}

  proc cbOuter(response: string) {.discardable.} =
    iterator cbIter(): Future[int] {.closure.} =
      for i in 0..7:
        proc foo(): int =
          iterator fooIter(): Future[int] {.closure.} =
            echo response, i
            yield Future[int](data: 17)
          var iterVar = fooIter
          iterVar().data
        yield Future[int](data: foo())

    var iterVar2 = cbIter
    proc cb2() {.closure.} =
      try:
        if not finished(iterVar2):
          let next = iterVar2()
          if next != nil:
            next.callback = cb2
      except:
        echo "WTF"
    cb2()

  cbOuter "baro"



block tnamedparamanonproc:
  type
    PButton = ref object
    TButtonClicked = proc(button: PButton) {.nimcall.}

  proc newButton(onClick: TButtonClicked) =
    discard

  proc main() =
    newButton(onClick = proc(b: PButton) =
      var requestomat = 12
      )

  main()



block tnestedclosure:
  proc main(param: int) =
    var foo = 23
    proc outer(outerParam: string) =
      var outerVar = 88
      echo outerParam, outerVar
      proc inner() =
        block Test:
          echo foo, " ", param, outerParam, " ", outerVar
      inner()
    outer("foo")

  # test simple closure within dummy 'main':
  proc dummy =
    proc main2(param: int) =
      var fooB = 23
      proc outer(outerParam: string) =
        var outerVar = 88
        echo outerParam, outerVar
        proc inner() =
          block Test:
            echo fooB, " ", param, outerParam, " ", outerVar
        inner()
      outer("foo")
    main2(24)

  dummy()

  main(24)

  # Jester + async triggered this bug:
  proc cbOuter() =
    var response = "hohoho"
    block:
      proc cbIter() =
        block:
          proc fooIter() =
            doAssert response == "hohoho"
          fooIter()
      cbIter()
  cbOuter()



block tnestedproc:
  proc p(x, y: int): int =
    result = x + y

  echo p((proc (): int =
            var x = 7
            return x)(),
         (proc (): int = return 4)())



block tnoclosure:
  proc pascal(n: int) =
    var row = @[1]
    for r in 1..n:
      row = zip(row & @[0], @[0] & row).mapIt(it[0] + it[1])
    echo row
  pascal(10)
