| /* | |
| * 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") | |
| } | |
| } |