-
-
Notifications
You must be signed in to change notification settings - Fork 89
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Adds RecursiveMethod attribute and handling for generating and managing an `object[]` stack which stores the values of locals in a method along with intermediate values that are important to a function call.
Showing
11 changed files
with
1,670 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
184 changes: 184 additions & 0 deletions
184
Assets/UdonSharp/Tests/TestScripts/FlowControl/RecursionTest.asset
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
%YAML 1.1 | ||
%TAG !u! tag:unity3d.com,2011: | ||
--- !u!114 &11400000 | ||
MonoBehaviour: | ||
m_ObjectHideFlags: 0 | ||
m_CorrespondingSourceObject: {fileID: 0} | ||
m_PrefabInstance: {fileID: 0} | ||
m_PrefabAsset: {fileID: 0} | ||
m_GameObject: {fileID: 0} | ||
m_Enabled: 1 | ||
m_EditorHideFlags: 0 | ||
m_Script: {fileID: 11500000, guid: c333ccfdd0cbdbc4ca30cef2dd6e6b9b, type: 3} | ||
m_Name: RecursionTest | ||
m_EditorClassIdentifier: | ||
serializedUdonProgramAsset: {fileID: 11400000, guid: 989e6fff91f0e554687b35b4ab4ad384, | ||
type: 2} | ||
udonAssembly: | ||
assemblyError: | ||
sourceCsScript: {fileID: 11500000, guid: 41aa655b07e37a84292568dd0dc1aac5, type: 3} | ||
behaviourIDHeapVarName: __refl_const_intnl_udonTypeID | ||
compileErrors: [] | ||
hasInteractEvent: 0 | ||
serializationData: | ||
SerializedFormat: 2 | ||
SerializedBytes: | ||
ReferencedUnityObjects: | ||
- {fileID: 11500000, guid: 11d8d463c5030e74bbaa9da5236e94e9, type: 3} | ||
SerializedBytesString: | ||
Prefab: {fileID: 0} | ||
PrefabModificationsReferencedUnityObjects: [] | ||
PrefabModifications: [] | ||
SerializationNodes: | ||
- Name: fieldDefinitions | ||
Entry: 7 | ||
Data: 0|System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[UdonSharp.Compiler.FieldDefinition, | ||
UdonSharp.Editor]], mscorlib | ||
- Name: comparer | ||
Entry: 7 | ||
Data: 1|System.Collections.Generic.GenericEqualityComparer`1[[System.String, | ||
mscorlib]], mscorlib | ||
- Name: | ||
Entry: 8 | ||
Data: | ||
- Name: | ||
Entry: 12 | ||
Data: 2 | ||
- Name: | ||
Entry: 7 | ||
Data: | ||
- Name: $k | ||
Entry: 1 | ||
Data: tester | ||
- Name: $v | ||
Entry: 7 | ||
Data: 2|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor | ||
- Name: fieldSymbol | ||
Entry: 7 | ||
Data: 3|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor | ||
- Name: internalType | ||
Entry: 7 | ||
Data: 4|System.RuntimeType, mscorlib | ||
- Name: | ||
Entry: 1 | ||
Data: UdonSharp.Tests.IntegrationTestSuite, Assembly-CSharp | ||
- Name: | ||
Entry: 8 | ||
Data: | ||
- Name: declarationType | ||
Entry: 3 | ||
Data: 2 | ||
- Name: syncMode | ||
Entry: 3 | ||
Data: 0 | ||
- Name: symbolResolvedTypeName | ||
Entry: 1 | ||
Data: VRCUdonUdonBehaviour | ||
- Name: symbolOriginalName | ||
Entry: 1 | ||
Data: tester | ||
- Name: symbolUniqueName | ||
Entry: 1 | ||
Data: tester | ||
- Name: symbolDefaultValue | ||
Entry: 6 | ||
Data: | ||
- Name: | ||
Entry: 8 | ||
Data: | ||
- Name: fieldAttributes | ||
Entry: 7 | ||
Data: 5|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib | ||
- Name: | ||
Entry: 12 | ||
Data: 1 | ||
- Name: | ||
Entry: 7 | ||
Data: 6|System.NonSerializedAttribute, mscorlib | ||
- Name: | ||
Entry: 8 | ||
Data: | ||
- Name: | ||
Entry: 13 | ||
Data: | ||
- Name: | ||
Entry: 8 | ||
Data: | ||
- Name: userBehaviourSource | ||
Entry: 10 | ||
Data: 0 | ||
- Name: | ||
Entry: 8 | ||
Data: | ||
- Name: | ||
Entry: 8 | ||
Data: | ||
- Name: | ||
Entry: 7 | ||
Data: | ||
- Name: $k | ||
Entry: 1 | ||
Data: externChildCount | ||
- Name: $v | ||
Entry: 7 | ||
Data: 7|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor | ||
- Name: fieldSymbol | ||
Entry: 7 | ||
Data: 8|UdonSharp.Compiler.SymbolDefinition, UdonSharp.Editor | ||
- Name: internalType | ||
Entry: 7 | ||
Data: 9|System.RuntimeType, mscorlib | ||
- Name: | ||
Entry: 1 | ||
Data: System.Int32, mscorlib | ||
- Name: | ||
Entry: 8 | ||
Data: | ||
- Name: declarationType | ||
Entry: 3 | ||
Data: 2 | ||
- Name: syncMode | ||
Entry: 3 | ||
Data: 0 | ||
- Name: symbolResolvedTypeName | ||
Entry: 1 | ||
Data: SystemInt32 | ||
- Name: symbolOriginalName | ||
Entry: 1 | ||
Data: externChildCount | ||
- Name: symbolUniqueName | ||
Entry: 1 | ||
Data: externChildCount | ||
- Name: symbolDefaultValue | ||
Entry: 6 | ||
Data: | ||
- Name: | ||
Entry: 8 | ||
Data: | ||
- Name: fieldAttributes | ||
Entry: 7 | ||
Data: 10|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib | ||
- Name: | ||
Entry: 12 | ||
Data: 0 | ||
- Name: | ||
Entry: 13 | ||
Data: | ||
- Name: | ||
Entry: 8 | ||
Data: | ||
- Name: userBehaviourSource | ||
Entry: 6 | ||
Data: | ||
- Name: | ||
Entry: 8 | ||
Data: | ||
- Name: | ||
Entry: 8 | ||
Data: | ||
- Name: | ||
Entry: 13 | ||
Data: | ||
- Name: | ||
Entry: 8 | ||
Data: |
8 changes: 8 additions & 0 deletions
8
Assets/UdonSharp/Tests/TestScripts/FlowControl/RecursionTest.asset.meta
Oops, something went wrong.
245 changes: 245 additions & 0 deletions
245
Assets/UdonSharp/Tests/TestScripts/FlowControl/RecursionTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,245 @@ | ||
| ||
using UdonSharp; | ||
using UnityEngine; | ||
using VRC.SDKBase; | ||
using VRC.Udon; | ||
|
||
namespace UdonSharp.Tests | ||
{ | ||
[AddComponentMenu("Udon Sharp/Tests/RecursionTest")] | ||
public class RecursionTest : UdonSharpBehaviour | ||
{ | ||
[System.NonSerialized] | ||
public IntegrationTestSuite tester; | ||
|
||
[RecursiveMethod] | ||
int Factorial(int input) | ||
{ | ||
if (input == 1) | ||
return 1; | ||
|
||
return input * Factorial(input - 1); | ||
} | ||
|
||
int Partition(int[] arr, int left, int right) | ||
{ | ||
int pivot; | ||
pivot = arr[left]; | ||
while (true) | ||
{ | ||
while (arr[left] < pivot) | ||
{ | ||
left++; | ||
} | ||
while (arr[right] > pivot) | ||
{ | ||
right--; | ||
} | ||
if (left < right) | ||
{ | ||
int temp = arr[right]; | ||
arr[right] = arr[left]; | ||
arr[left] = temp; | ||
} | ||
else | ||
{ | ||
return right; | ||
} | ||
} | ||
} | ||
|
||
// Copy paste from https://www.tutorialspoint.com/chash-program-to-perform-quick-sort-using-recursion | ||
[RecursiveMethod] | ||
public void QuickSort(int[] arr, int left, int right) | ||
{ | ||
int pivot; | ||
if (left < right) | ||
{ | ||
pivot = Partition(arr, left, right); | ||
|
||
if (pivot > 1) | ||
QuickSort(arr, left, pivot - 1); | ||
|
||
if (pivot + 1 < right) | ||
QuickSort(arr, pivot + 1, right); | ||
} | ||
|
||
arr = null; // Just throw a curveball with something that should be handled, but could break stuff if it isn't handled | ||
} | ||
|
||
// https://www.geeksforgeeks.org/iterative-quick-sort/ | ||
// Just a test for relative performance of using recursive vs iterative | ||
//void QuickSortIterative(int[] arr, int l, int h) | ||
//{ | ||
// int[] stack = new int[h - l + 1]; | ||
|
||
// int top = -1; | ||
|
||
// stack[++top] = l; | ||
// stack[++top] = h; | ||
|
||
// while (top >= 0) | ||
// { | ||
// h = stack[top--]; | ||
// l = stack[top--]; | ||
|
||
// int p = Partition(arr, l, h); | ||
|
||
// if (p - 1 > l) | ||
// { | ||
// stack[++top] = l; | ||
// stack[++top] = p - 1; | ||
// } | ||
|
||
// if (p + 1 < h) | ||
// { | ||
// stack[++top] = p + 1; | ||
// stack[++top] = h; | ||
// } | ||
// } | ||
//} | ||
|
||
int[] InitTestArray(int size) | ||
{ | ||
int[] testArray = new int[size]; | ||
|
||
for (int i = 0; i < testArray.Length; ++i) | ||
testArray[i] = i; | ||
|
||
return testArray; | ||
} | ||
|
||
void ShuffleArray(int[] shuffleArray) | ||
{ | ||
Random.InitState(1337); | ||
|
||
int n = shuffleArray.Length - 1; | ||
for (int i = 0; i < n; ++i) | ||
{ | ||
int r = Random.Range(i + 1, n); | ||
int flipVal = shuffleArray[r]; | ||
shuffleArray[r] = shuffleArray[i]; | ||
shuffleArray[i] = flipVal; | ||
} | ||
} | ||
|
||
bool IsSorted(int[] array) | ||
{ | ||
for (int i = 0; i < array.Length; ++i) | ||
{ | ||
if (array[i] != i) | ||
{ | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
|
||
[RecursiveMethod] | ||
public string CombineStrings(int count, string a, string b) | ||
{ | ||
if (count == 0) | ||
return ""; | ||
|
||
return string.Concat(a, CombineStrings(count - 1, b, a), CombineStrings(count - 1, a, b)); | ||
} | ||
|
||
[RecursiveMethod] | ||
public string CombineStringsExtern(int count, string a, string b) | ||
{ | ||
if (count == 0) | ||
return ""; | ||
|
||
RecursionTest self = this; | ||
|
||
//Debug.Log($"count: {count}, a: {a}, b: {b}"/*, a result: {aResult}, b result: {bResult}"*/); | ||
|
||
return string.Concat(a, self.CombineStringsExtern(count - 1, b, a), self.CombineStringsExtern(count - 1, a, b)); | ||
} | ||
|
||
[RecursiveMethod] | ||
public string CombineStringsParams(int count, string a, string b, string c, string d, string e) | ||
{ | ||
if (count == 0) | ||
return ""; | ||
|
||
return string.Concat(a, CombineStringsParams(count - 1, e, d, c, b, a), CombineStringsParams(count - 1, a, b, c, d, e), CombineStringsParams(count - 1, a, a, c, d, e), CombineStringsParams(count - 1, a, b, b, e, e), CombineStringsParams(count - 1, a, b, a, d, e)); | ||
} | ||
|
||
[RecursiveMethod] | ||
public string CombineStringsNested(int count, string a, string b) | ||
{ | ||
if (count == 0) | ||
return ""; | ||
|
||
return string.Concat(a, CombineStringsNested(count - 1, CombineStringsNested(count - 1, b, a), CombineStringsNested(count - 1, a, b)), CombineStringsNested(count - 1, CombineStringsNested(count - 1, a, b), CombineStringsNested(count - 1, b, a)), "c"); | ||
} | ||
|
||
//public void Start() | ||
//{ | ||
// ExecuteTests(); | ||
//} | ||
|
||
[RecursiveMethod] | ||
int CountChildren(Transform transformToCount) | ||
{ | ||
int childCount = transformToCount.childCount; | ||
|
||
foreach (Transform child in transformToCount) | ||
childCount += CountChildren(child); | ||
|
||
return childCount; | ||
} | ||
|
||
int externChildCount; | ||
|
||
[RecursiveMethod] | ||
void CountChildrenExternalCount(Transform transformToCount) | ||
{ | ||
externChildCount += transformToCount.childCount; | ||
|
||
foreach (Transform child in transformToCount) | ||
CountChildrenExternalCount(child); | ||
} | ||
|
||
public void ExecuteTests() | ||
{ | ||
tester.TestAssertion("Basic recursion 4!", Factorial(4) == 24); | ||
tester.TestAssertion("Basic recursion 5!", Factorial(5) == 120); | ||
tester.TestAssertion("Basic recursion 12!", Factorial(12) == 479001600); | ||
|
||
int arraySize = Random.Range(10000, 11000); | ||
int[] shuffleArray = InitTestArray(arraySize); // Fuzz a little | ||
|
||
ShuffleArray(shuffleArray); | ||
QuickSort(shuffleArray, 0, shuffleArray.Length - 1); | ||
|
||
bool sorted = IsSorted(shuffleArray); | ||
if (!sorted) | ||
Debug.LogWarning($"Array size that failed {arraySize}"); | ||
|
||
tester.TestAssertion("Quicksort recursion", sorted); | ||
|
||
RecursionTest self = this; | ||
|
||
ShuffleArray(shuffleArray); | ||
self.QuickSort(shuffleArray, 0, shuffleArray.Length - 1); | ||
|
||
tester.TestAssertion("Quicksort external call", IsSorted(shuffleArray)); | ||
|
||
tester.TestAssertion("Function parameter swap recursion", CombineStrings(6, "a", "b") == "abababababababababababababababababababababababababababababababa"); | ||
|
||
tester.TestAssertion("Function parameter swap recursion external call", self.CombineStringsExtern(6, "a", "b") == "abababababababababababababababababababababababababababababababa"); | ||
tester.TestAssertion("Params array recursion", CombineStringsParams(4, "a", "b", "c", "d", "e") == "aeaeaaaaeaeeeeeaeeeeeaeeeeeaeeeeaeaeeeeaeaaaaaeaaaaaeaaaaaeaaaaaeaeeeeaeaaaaaeaaaaaeaaaaaeaaaaaeaeeeeaeaaaaaeaaaaaeaaaaaeaaaaaeaeeeeaeaaaaaeaaaaaeaaaaaeaaaa"); | ||
tester.TestAssertion("Nested call recursion", CombineStringsNested(3, "a", "b") == "abaccbcccabccacccccbaccbccccccabccacccbaccbcccccabccaccccccc"); | ||
|
||
tester.TestAssertion("Count children recursively foreach", CountChildren(transform) == 20); | ||
|
||
externChildCount = 0; | ||
CountChildrenExternalCount(transform); | ||
|
||
tester.TestAssertion("Count children recursively foreach external counter", externChildCount == 20); | ||
} | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
Assets/UdonSharp/Tests/TestScripts/FlowControl/RecursionTest.cs.meta
Oops, something went wrong.