// run

// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Test the 'for range' construct ranging over functions.

package main

var gj int

func yield4x(yield func() bool) {
	_ = yield() && yield() && yield() && yield()
}

func yield4(yield func(int) bool) {
	_ = yield(1) && yield(2) && yield(3) && yield(4)
}

func yield3(yield func(int) bool) {
	_ = yield(1) && yield(2) && yield(3)
}

func yield2(yield func(int) bool) {
	_ = yield(1) && yield(2)
}

func testfunc0() {
	j := 0
	for range yield4x {
		j++
	}
	if j != 4 {
		println("wrong count ranging over yield4x:", j)
		panic("testfunc0")
	}

	j = 0
	for _ = range yield4 {
		j++
	}
	if j != 4 {
		println("wrong count ranging over yield4:", j)
		panic("testfunc0")
	}
}

func testfunc1() {
	bad := false
	j := 1
	for i := range yield4 {
		if i != j {
			println("range var", i, "want", j)
			bad = true
		}
		j++
	}
	if j != 5 {
		println("wrong count ranging over f:", j)
		bad = true
	}
	if bad {
		panic("testfunc1")
	}
}

func testfunc2() {
	bad := false
	j := 1
	var i int
	for i = range yield4 {
		if i != j {
			println("range var", i, "want", j)
			bad = true
		}
		j++
	}
	if j != 5 {
		println("wrong count ranging over f:", j)
		bad = true
	}
	if i != 4 {
		println("wrong final i ranging over f:", i)
		bad = true
	}
	if bad {
		panic("testfunc2")
	}
}

func testfunc3() {
	bad := false
	j := 1
	var i int
	for i = range yield4 {
		if i != j {
			println("range var", i, "want", j)
			bad = true
		}
		j++
		if i == 2 {
			break
		}
		continue
	}
	if j != 3 {
		println("wrong count ranging over f:", j)
		bad = true
	}
	if i != 2 {
		println("wrong final i ranging over f:", i)
		bad = true
	}
	if bad {
		panic("testfunc3")
	}
}

func testfunc4() {
	bad := false
	j := 1
	var i int
	func() {
		for i = range yield4 {
			if i != j {
				println("range var", i, "want", j)
				bad = true
			}
			j++
			if i == 2 {
				return
			}
		}
	}()
	if j != 3 {
		println("wrong count ranging over f:", j)
		bad = true
	}
	if i != 2 {
		println("wrong final i ranging over f:", i)
		bad = true
	}
	if bad {
		panic("testfunc3")
	}
}

func func5() (int, int) {
	for i := range yield4 {
		return 10, i
	}
	panic("still here")
}

func testfunc5() {
	x, y := func5()
	if x != 10 || y != 1 {
		println("wrong results", x, y, "want", 10, 1)
		panic("testfunc5")
	}
}

func func6() (z, w int) {
	for i := range yield4 {
		z = 10
		w = i
		return
	}
	panic("still here")
}

func testfunc6() {
	x, y := func6()
	if x != 10 || y != 1 {
		println("wrong results", x, y, "want", 10, 1)
		panic("testfunc6")
	}
}

var saved []int

func save(x int) {
	saved = append(saved, x)
}

func printslice(s []int) {
	print("[")
	for i, x := range s {
		if i > 0 {
			print(", ")
		}
		print(x)
	}
	print("]")
}

func eqslice(s, t []int) bool {
	if len(s) != len(t) {
		return false
	}
	for i, x := range s {
		if x != t[i] {
			return false
		}
	}
	return true
}

func func7() {
	defer save(-1)
	for i := range yield4 {
		defer save(i)
	}
	defer save(5)
}

func checkslice(name string, saved, want []int) {
	if !eqslice(saved, want) {
		print("wrong results ")
		printslice(saved)
		print(" want ")
		printslice(want)
		print("\n")
		panic(name)
	}
}

func testfunc7() {
	saved = nil
	func7()
	want := []int{5, 4, 3, 2, 1, -1}
	checkslice("testfunc7", saved, want)
}

func func8() {
	defer save(-1)
	for i := range yield2 {
		for j := range yield3 {
			defer save(i*10 + j)
		}
		defer save(i)
	}
	defer save(-2)
	for i := range yield4 {
		defer save(i)
	}
	defer save(-3)
}

func testfunc8() {
	saved = nil
	func8()
	want := []int{-3, 4, 3, 2, 1, -2, 2, 23, 22, 21, 1, 13, 12, 11, -1}
	checkslice("testfunc8", saved, want)
}

func func9() {
	n := 0
	for _ = range yield2 {
		for _ = range yield3 {
			n++
			defer save(n)
		}
	}
}

func testfunc9() {
	saved = nil
	func9()
	want := []int{6, 5, 4, 3, 2, 1}
	checkslice("testfunc9", saved, want)
}

// test that range evaluates the index and value expressions
// exactly once per iteration.

var ncalls = 0

func getvar(p *int) *int {
	ncalls++
	return p
}

func iter2(list ...int) func(func(int, int) bool) {
	return func(yield func(int, int) bool) {
		for i, x := range list {
			if !yield(i, x) {
				return
			}
		}
	}
}

func testcalls() {
	var i, v int
	ncalls = 0
	si := 0
	sv := 0
	for *getvar(&i), *getvar(&v) = range iter2(1, 2) {
		si += i
		sv += v
	}
	if ncalls != 4 {
		println("wrong number of calls:", ncalls, "!= 4")
		panic("fail")
	}
	if si != 1 || sv != 3 {
		println("wrong sum in testcalls", si, sv)
		panic("fail")
	}
}

type iter3YieldFunc func(int, int) bool

func iter3(list ...int) func(iter3YieldFunc) {
	return func(yield iter3YieldFunc) {
		for k, v := range list {
			if !yield(k, v) {
				return
			}
		}
	}
}

func testcalls1() {
	ncalls := 0
	for k, v := range iter3(1, 2, 3) {
		_, _ = k, v
		ncalls++
	}
	if ncalls != 3 {
		println("wrong number of calls:", ncalls, "!= 3")
		panic("fail")
	}
}

func main() {
	testfunc0()
	testfunc1()
	testfunc2()
	testfunc3()
	testfunc4()
	testfunc5()
	testfunc6()
	testfunc7()
	testfunc8()
	testfunc9()
	testcalls()
	testcalls1()
}
