blob: 50efacb03e311fa8655748b19b7b36dcbc010c45 [file] [log] [blame]
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package test.utils
import kotlin.test.*
class DeepRecursiveTest {
@Test
fun testSimpleReturn() {
// just returns a value without any recursive calls
val ok = DeepRecursiveFunction<Int, String> { i -> "Ok$i" }
assertEquals("Ok42", ok(42))
}
@Test
fun testDeepTreeDepth() {
val n = 100_000
assertEquals(n, depth(deepTree(n)))
}
@Test
fun testBinaryTreeDepth() {
val k = 15
assertEquals(k, depth(binaryTree(k)))
}
private class MutualRec {
val even: DeepRecursiveFunction<Tree?, Int> = DeepRecursiveFunction { t ->
if (t == null) 0 else odd.callRecursive(t.left) + odd.callRecursive(t.right) + 1
}
val odd: DeepRecursiveFunction<Tree?, Int> = DeepRecursiveFunction { t ->
if (t == null) 0 else even.callRecursive(t.left) + even.callRecursive(t.right)
}
}
@Test
fun testDeepTreeOddEvenNodesMutual() {
val n = 50_000
val dt = deepTree(n)
val rec = MutualRec()
assertEquals(n / 2, rec.even(dt))
assertEquals(n / 2, rec.odd(dt))
}
@Test
fun testBinaryTreeOddEvenNodesMutual() {
val k = 15
val bt = binaryTree(k)
val rec = MutualRec()
assertEquals(21845, rec.even(bt))
assertEquals(10922, rec.odd(bt))
}
private class MutualAndDirectMixRec {
val b: DeepRecursiveFunction<Int, String> = DeepRecursiveFunction { i -> "b$i" }
val a: DeepRecursiveFunction<Int, String> = DeepRecursiveFunction { i ->
when (i) {
// mix callRecursive calls to other function and in this context
0 -> b.callRecursive(1) + callRecursive(2) + aa().callRecursive(3)
else -> "a$i"
}
}
fun aa() = a
}
@Test
fun testMutualAndDirectMix() {
// mix of callRecursion on this scope and on other DRF
val rec = MutualAndDirectMixRec()
val s = rec.a.invoke(0)
assertEquals("b1a2a3", s)
}
private class EqualToAnythingClassRec {
var nullCount = 0
val a: DeepRecursiveFunction<Tree?, EqualToAnything> = DeepRecursiveFunction { t ->
if (t == null) EqualToAnything(nullCount++) else b.callRecursive(t.left)
}
val b: DeepRecursiveFunction<Tree?, EqualToAnything> = DeepRecursiveFunction { t ->
if (t == null) EqualToAnything(nullCount++) else a.callRecursive(t.left)
}
}
@Test
fun testEqualToAnythingClass() {
// Mutually recursive tail calls & broken equals
val rec = EqualToAnythingClassRec()
val result = rec.a.invoke(deepTree(100))
assertEquals(1, rec.nullCount)
assertEquals(0, result.i)
}
@Test
fun testBadClass() {
val compute = object {
val a: DeepRecursiveFunction<Bad, Bad> = DeepRecursiveFunction { v -> Bad(v.i + 1) }
val b: DeepRecursiveFunction<Bad, Bad> = DeepRecursiveFunction { v ->
when (v.i) {
0 -> callRecursive(Bad(1))
1 -> Bad(a.callRecursive(Bad(19)).i + callRecursive(Bad(2)).i)
2 -> Bad(a.callRecursive(Bad(20)).i + 1)
else -> error("Cannot happen")
}
}
}
assertEquals(42, compute.b(Bad(0)).i)
}
private class Tree(val left: Tree? = null, val right: Tree? = null)
private fun deepTree(n: Int) = generateSequence(Tree()) { Tree(it) }.take(n).last()
private fun binaryTree(k: Int): Tree? =
if (k == 0) null else Tree(binaryTree(k - 1), binaryTree(k - 1))
private val depth = DeepRecursiveFunction<Tree?, Int> { t ->
if (t == null) 0 else maxOf(
callRecursive(t.left),
callRecursive(t.right)
) + 1
}
// It is equals to any other class
private class EqualToAnything(val i: Int) {
override fun equals(other: Any?): Boolean = true
override fun toString(): String = "OK"
}
// Throws exception on all object methods
private class Bad(val i: Int) {
override fun equals(other: Any?): Boolean = error("BAD")
override fun hashCode(): Int = error("BAD")
override fun toString(): String = error("BAD")
}
}