public void CatchesSimpleImportCollision()
    {
        var fileSystem = new InMemoryFileProvider();

        fileSystem.RegisterFileWithIdentifier("root.lsystem", @"
#axiom Y
#iterations 1000
#symbols XY

#include lib0.lsyslib (Exported->X)
#include lib1.lsyslib (Exported->X)
");

        fileSystem.RegisterFileWithIdentifier("lib0.lsyslib", @"
#symbols AB
#export Exported A
");
        fileSystem.RegisterFileWithIdentifier("lib1.lsyslib", @"
#symbols CD
#export Exported C
");
        var linker = new FileLinker(fileSystem);

        try
        {
            linker.LinkFiles("root.lsystem");
        }
        catch (LinkException e)
        {
            Assert.AreEqual(LinkExceptionType.IMPORT_COLLISION, e.exceptionType);
            return;
        }
        Assert.Fail("linker must prevent importing symbols in such a way that it attempts to represent multiple symbols with the same character");
    }
    public void CatchesMissingExport()
    {
        var fileSystem = new InMemoryFileProvider();

        fileSystem.RegisterFileWithIdentifier("root.lsystem", @"
#axiom Y
#iterations 1000
#symbols XYZ

#include lib0.lsyslib (NotExported->X)
");

        fileSystem.RegisterFileWithIdentifier("lib0.lsyslib", @"
#symbols AB
#export Exported A
");
        var linker = new FileLinker(fileSystem);

        try
        {
            linker.LinkFiles("root.lsystem");
        }
        catch (LinkException e)
        {
            Assert.AreEqual(LinkExceptionType.MISSING_EXPORT, e.exceptionType);
            return;
        }
        Assert.Fail("linker must prevent importing symbols which are not exported");
    }
    public void CatchesRunTimeVariableCollision()
    {
        var fileSystem = new InMemoryFileProvider();

        fileSystem.RegisterFileWithIdentifier("root.lsystem", @"
#axiom Y
#iterations 1000
#symbols XY

#runtime runTime 1992.01
#include lib0.lsyslib
");

        fileSystem.RegisterFileWithIdentifier("lib0.lsyslib", @"
#symbols AB
#runtime runTime 1992.01
#export Exported A
");
        var linker = new FileLinker(fileSystem);

        try
        {
            linker.LinkFiles("root.lsystem");
        }
        catch (LinkException e)
        {
            Assert.AreEqual(LinkExceptionType.GLOBAL_VARIABLE_COLLISION, e.exceptionType);
            return;
        }
        Assert.Fail("linker must prevent declaration of multiple global runtime variables with the same name");
    }
    public void CatchesMissingInclude()
    {
        var fileSystem = new InMemoryFileProvider();

        fileSystem.RegisterFileWithIdentifier("root.lsystem", @"
#axiom Y
#iterations 1000
#symbols XYZ

#include missinglib.lsyslib
");

        fileSystem.RegisterFileWithIdentifier("lib0.lsyslib", @"
#symbols AB
");
        var linker = new FileLinker(fileSystem);

        try
        {
            linker.LinkFiles("root.lsystem");
        }
        catch (LinkException e)
        {
            Assert.AreEqual(LinkExceptionType.MISSING_FILE, e.exceptionType);
            return;
        }
        Assert.Fail("linker must gracefully fail when it cannot find a file");
    }
    public void AllowsImportCollisionToPreventDissonance()
    {
        var fileSystem = new InMemoryFileProvider();

        fileSystem.RegisterFileWithIdentifier("root.lsystem", @"
#axiom Y
#iterations 1000
#symbols XY

#include lib0.lsyslib (Exported->X)
#include lib1.lsyslib (Exported->X)
");

        fileSystem.RegisterFileWithIdentifier("lib0.lsyslib", @"
#symbols AB
#include lib2.lsyslib (Exported->A)
#export Exported A
");
        fileSystem.RegisterFileWithIdentifier("lib1.lsyslib", @"
#symbols CD
#include lib2.lsyslib (Exported->C)
#export Exported C
");
        fileSystem.RegisterFileWithIdentifier("lib2.lsyslib", @"
#symbols EF
#export Exported E
");
        var linker    = new FileLinker(fileSystem);
        var linkedSet = linker.LinkFiles("root.lsystem");

        var rootFile = linkedSet.allFiles.data[linkedSet.fileIndexesByFullIdentifier["root.lsystem"]];

        Assert.AreEqual(4, rootFile.allSymbolAssignments.Count);
        Assert.AreEqual(4, rootFile.allSymbolAssignments.Select(x => x.sourceCharacter).Intersect("XY[]").Count());

        var lib0File = linkedSet.allFiles.data[linkedSet.fileIndexesByFullIdentifier["lib0.lsyslib"]];

        Assert.AreEqual(4, lib0File.allSymbolAssignments.Count);
        Assert.AreEqual(4, lib0File.allSymbolAssignments.Select(x => x.sourceCharacter).Intersect("AB[]").Count());

        var lib1File = linkedSet.allFiles.data[linkedSet.fileIndexesByFullIdentifier["lib1.lsyslib"]];

        Assert.AreEqual(4, lib1File.allSymbolAssignments.Count);
        Assert.AreEqual(4, lib1File.allSymbolAssignments.Select(x => x.sourceCharacter).Intersect("CD[]").Count());

        var lib2File = linkedSet.allFiles.data[linkedSet.fileIndexesByFullIdentifier["lib2.lsyslib"]];

        Assert.AreEqual(4, lib2File.allSymbolAssignments.Count);
        Assert.AreEqual(4, lib2File.allSymbolAssignments.Select(x => x.sourceCharacter).Intersect("EF[]").Count());

        var importedInRoot = rootFile.GetSymbolInFile('X');
        var exportedInLib0 = lib0File.GetSymbolInFile('A');
        var exportedInLib1 = lib1File.GetSymbolInFile('C');
        var exportedInLib2 = lib2File.GetSymbolInFile('E');

        Assert.AreEqual(exportedInLib2, exportedInLib1);
        Assert.AreEqual(exportedInLib2, exportedInLib0);
        Assert.AreEqual(exportedInLib2, importedInRoot);
    }
    public void LinksSimpleDependencyAndExecutes()
    {
        var fileSystem = new InMemoryFileProvider();

        fileSystem.RegisterFileWithIdentifier("root.lsystem", @"
#axiom Y
#iterations 10
#symbols XY

#include lib.lsyslib (Exported->X)
Y -> YX
");

        fileSystem.RegisterFileWithIdentifier("lib.lsyslib", @"
#symbols AB
#export Exported B

B -> AB
");
        var linker      = new FileLinker(fileSystem);
        var linkedFiles = linker.LinkFiles("root.lsystem");

        using var system = linkedFiles.CompileSystem();
        LSystemState <float> currentState = new DefaultLSystemState(
            linkedFiles.GetAxiom(),
            (uint)UnityEngine.Random.Range(int.MinValue, int.MaxValue));

        var X = linkedFiles.GetSymbol("root.lsystem", 'X');
        var Y = linkedFiles.GetSymbol("root.lsystem", 'Y');
        var A = linkedFiles.GetSymbol("lib.lsyslib", 'A');
        var B = linkedFiles.GetSymbol("lib.lsyslib", 'B');

        Assert.AreEqual(B, X);

        var symbolStringMapping = new Dictionary <int, char>()
        {
            { X, 'X' },
            { Y, 'Y' },
            { A, 'A' }
        };

        /**
         * system equivalent with remapped symbol names:
         * Y -> YX
         * X -> AX
         */

        Assert.AreEqual("Y", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("YX", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("YXAX", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("YXAXAAX", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("YXAXAAXAAAX", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState.currentSymbols.DisposeImmediate();
    }
    public void LinksBuiltinLibraryAndExecutes()
    {
        var builtins = new BuiltinLibraries();

        builtins.Add(new DiffusionLibrary());

        var fileSystem = new InMemoryFileProvider(builtins);

        fileSystem.RegisterFileWithIdentifier("root.lsystem", @"
#axiom n(0.5, 0, 10)ST(3)
#iterations 10
#symbols naFTS

#include diffusion (Node->n) (Amount->a)

T(x) : x > 0 -> n(.5, 0, 10)FT(x - 1)
S -> a(3)S
");
        var linker      = new FileLinker(fileSystem);
        var linkedFiles = linker.LinkFiles("root.lsystem");

        using var system = linkedFiles.CompileSystem();
        LSystemState <float> currentState = new DefaultLSystemState(
            linkedFiles.GetAxiom(),
            (uint)UnityEngine.Random.Range(int.MinValue, int.MaxValue));

        var n = linkedFiles.GetSymbol("root.lsystem", 'n');
        var a = linkedFiles.GetSymbol("root.lsystem", 'a');
        var F = linkedFiles.GetSymbol("root.lsystem", 'F');
        var T = linkedFiles.GetSymbol("root.lsystem", 'T');
        var S = linkedFiles.GetSymbol("root.lsystem", 'S');

        var symbolStringMapping = new Dictionary <int, char>()
        {
            { n, 'n' },
            { a, 'a' },
            { F, 'F' },
            { T, 'T' },
            { S, 'S' },
        };

        Assert.AreEqual("n(0.5, 0, 10)ST(3)", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("n(0.5, 0, 10)a(3)Sn(0.5, 0, 10)FT(2)", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("n(0.5, 1.5, 10)aa(3)Sn(0.5, 1.5, 10)Fn(0.5, 0, 10)FT(1)", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("n(0.5, 3, 10)aa(3)Sn(0.5, 2.25, 10)Fn(0.5, 0.75, 10)Fn(0.5, 0, 10)FT(0)", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("n(0.5, 4.125, 10)aa(3)Sn(0.5, 3.375, 10)Fn(0.5, 1.125, 10)Fn(0.5, 0.375, 10)FT(0)", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("n(0.5, 5.25, 10)aa(3)Sn(0.5, 4.125, 10)Fn(0.5, 1.875, 10)Fn(0.5, 0.75, 10)FT(0)", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("n(0.5, 6.1875, 10)aa(3)Sn(0.5, 5.0625, 10)Fn(0.5, 2.4375, 10)Fn(0.5, 1.3125, 10)FT(0)", currentState.currentSymbols.Data.ToString(symbolStringMapping));

        currentState.currentSymbols.DisposeImmediate();
    }
    public void LinksBuiltinLibraryWithCustomParameterAndExecutes()
    {
        var builtins = new BuiltinLibraries();

        builtins.Add(new DiffusionLibrary());

        var fileSystem = new InMemoryFileProvider(builtins);

        fileSystem.RegisterFileWithIdentifier("root.lsystem", @"
#axiom n(0.25, 18, 20)Fn(0.25, 0, 20)Fn(0.25, 0, 20)
#iterations 10
#symbols naF

#define diffusionStepsPerStep 2
#include diffusion (Node->n) (Amount->a)

");
        var linker      = new FileLinker(fileSystem);
        var linkedFiles = linker.LinkFiles("root.lsystem");

        using var system = linkedFiles.CompileSystem();
        LSystemState <float> currentState = new DefaultLSystemState(
            linkedFiles.GetAxiom(),
            (uint)UnityEngine.Random.Range(int.MinValue, int.MaxValue));

        var n = linkedFiles.GetSymbol("root.lsystem", 'n');
        var a = linkedFiles.GetSymbol("root.lsystem", 'a');
        var F = linkedFiles.GetSymbol("root.lsystem", 'F');

        var symbolStringMapping = new Dictionary <int, char>()
        {
            { n, 'n' },
            { a, 'a' },
            { F, 'F' },
        };

        Assert.AreEqual("n(0.25, 18, 20)Fn(0.25, 0, 20)Fn(0.25, 0, 20)", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("n(0.25, 11.25, 20)Fn(0.25, 5.625, 20)Fn(0.25, 1.125, 20)", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("n(0.25, 8.859375, 20)Fn(0.25, 5.976563, 20)Fn(0.25, 3.164063, 20)", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("n(0.25, 7.602539, 20)Fn(0.25, 5.998535, 20)Fn(0.25, 4.398926, 20)", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("n(0.25, 6.901062, 20)Fn(0.25, 5.999908, 20)Fn(0.25, 5.09903, 20)", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("n(0.25, 6.506824, 20)Fn(0.25, 5.999994, 20)Fn(0.25, 5.493181, 20)", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("n(0.25, 6.285088, 20)Fn(0.25, 6, 20)Fn(0.25, 5.714913, 20)", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("n(0.25, 6.160362, 20)Fn(0.25, 6, 20)Fn(0.25, 5.839638, 20)", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("n(0.25, 6.090203, 20)Fn(0.25, 6, 20)Fn(0.25, 5.909797, 20)", currentState.currentSymbols.Data.ToString(symbolStringMapping));

        currentState.currentSymbols.DisposeImmediate();
    }
    public void CompilesWithImmaturityMarkers()
    {
        var fileSystem = new InMemoryFileProvider();

        fileSystem.RegisterFileWithIdentifier("root.lsystem", @"
#axiom Y(2)
#iterations 20
#symbols XY

#immature Y
Y(x) : x > 0 -> Y(x - 1)
Y(x) : x <= 0 -> X
");
        var linker      = new FileLinker(fileSystem);
        var linkedFiles = linker.LinkFiles("root.lsystem");

        using var system = linkedFiles.CompileSystem();
        LSystemState <float> currentState = new DefaultLSystemState(
            linkedFiles.GetAxiom(),
            (uint)UnityEngine.Random.Range(int.MinValue, int.MaxValue));

        var X = linkedFiles.GetSymbol("root.lsystem", 'X');
        var Y = linkedFiles.GetSymbol("root.lsystem", 'Y');

        var symbolStringMapping = new Dictionary <int, char>()
        {
            { X, 'X' },
            { Y, 'Y' }
        };

        /**
         * system equivalent with remapped symbol names:
         * Y -> YX
         * X -> AX
         */

        Assert.AreEqual("Y(2)", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("Y(1)", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        Assert.IsTrue(currentState.hasImmatureSymbols);

        currentState = system.StepSystem(currentState);
        Assert.AreEqual("Y(0)", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        Assert.IsTrue(currentState.hasImmatureSymbols);

        currentState = system.StepSystem(currentState);
        Assert.AreEqual("X", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        Assert.IsFalse(currentState.hasImmatureSymbols);

        currentState = system.StepSystem(currentState);
        Assert.AreEqual("X", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        Assert.IsFalse(currentState.hasImmatureSymbols);

        currentState.currentSymbols.DisposeImmediate();
    }
Exemple #10
0
        public void LoadFromFilePath(string filePath)
        {
            if (!string.IsNullOrWhiteSpace(filePath))
            {
                var linker = new FileLinker(new FileSystemFileProvider());
                linkedFiles = linker.LinkFiles(filePath);

                // compile the system right away and throw it out to catch any compile-time errors
                this.CompileToCached();
                compiledSystem?.Dispose();
                compiledSystem = null;
            }
        }
    public void LinkCompilesBranchingSymbolWithContextMatches()
    {
        var fileSystem = new InMemoryFileProvider();

        fileSystem.RegisterFileWithIdentifier("root.lsystem", @"
#axiom AF[FB]
#iterations 10
#symbols FAB
#matches AB

#symbols CD
#matches CD
    A > [B] -> C
A < B     -> D
");
        var linker      = new FileLinker(fileSystem);
        var linkedFiles = linker.LinkFiles("root.lsystem");

        using var system = linkedFiles.CompileSystem();
        LSystemState <float> currentState = new DefaultLSystemState(
            linkedFiles.GetAxiom(),
            (uint)UnityEngine.Random.Range(int.MinValue, int.MaxValue));

        var lBracket = linkedFiles.GetSymbol("root.lsystem", '[');
        var rBracket = linkedFiles.GetSymbol("root.lsystem", ']');
        var FRoot    = linkedFiles.GetSymbol("root.lsystem", 'F');
        var ARoot    = linkedFiles.GetSymbol("root.lsystem", 'A');
        var BRoot    = linkedFiles.GetSymbol("root.lsystem", 'B');
        var CRoot    = linkedFiles.GetSymbol("root.lsystem", 'C');
        var DRoot    = linkedFiles.GetSymbol("root.lsystem", 'D');


        var symbolStringMapping = new Dictionary <int, char>()
        {
            { lBracket, '[' },
            { rBracket, ']' },
            { FRoot, 'F' },
            { ARoot, 'A' },
            { BRoot, 'B' },
            { CRoot, 'C' },
            { DRoot, 'D' }
        };

        Assert.AreEqual("AF[FB]", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("CF[FD]", currentState.currentSymbols.Data.ToString(symbolStringMapping));


        currentState.currentSymbols.DisposeImmediate();
    }
Exemple #12
0
    public void TestLSystemFile(
        string fileText,
        IEnumerable <string> expectedSteps)
    {
        var fileSystem = new InMemoryFileProvider();

        fileSystem.RegisterFileWithIdentifier("root.lsystem", fileText);

        var linker      = new FileLinker(fileSystem);
        var linkedFiles = linker.LinkFiles("root.lsystem");

        using var system = linkedFiles.CompileSystem();
        LSystemState <float> state = new DefaultLSystemState(
            linkedFiles.GetAxiom(),
            (uint)UnityEngine.Random.Range(int.MinValue, int.MaxValue));

        var symbolStringMapping = linkedFiles.allSymbolDefinitionsLeafFirst
                                  .Where(x => x.sourceFileDefinition == "root.lsystem")
                                  .ToDictionary(x => x.actualSymbol, x => x.characterInSourceFile);

        using var axiom = linkedFiles.GetAxiom();
        Assert.AreEqual(axiom.ToString(symbolStringMapping), state.currentSymbols.Data.ToString(symbolStringMapping));
        try
        {
            foreach (var expectedStep in expectedSteps)
            {
                state = system.StepSystem(state);
                Assert.AreEqual(expectedStep, state.currentSymbols.Data.ToString(symbolStringMapping));
            }
        }
        catch (System.Exception e)
        {
            UnityEngine.Debug.LogException(e);
            UnityEngine.Debug.Log("actual result:");
            state.currentSymbols.DisposeImmediate();
            state = new DefaultLSystemState(
                linkedFiles.GetAxiom(),
                (uint)UnityEngine.Random.Range(int.MinValue, int.MaxValue));
            for (int i = 0; i < expectedSteps.Count(); i++)
            {
                state = system.StepSystem(state);
                UnityEngine.Debug.Log(state.currentSymbols.Data.ToString(symbolStringMapping));
            }
            throw;
        }
        finally
        {
            state.currentSymbols.DisposeImmediate();
        }
    }
Exemple #13
0
    public void DiffusionRateCanChangeAtRuntime()
    {
        var testFile = @"
#axiom n(0.5, 0, 10)Fn(0.5, 0, 10)Fn(0.5, 8, 10)
#iterations 10
#symbols Fna
#define diffusionStepsPerStep 1
#include diffusion (Node->n) (Amount->a)
";


        var fileSystem = new InMemoryFileProvider();

        fileSystem.RegisterFileWithIdentifier("root.lsystem", testFile);
        var linker      = new FileLinker(fileSystem);
        var linkedFiles = linker.LinkFiles("root.lsystem");

        using var system = linkedFiles.CompileSystem();

        LSystemState <float> state = new DefaultLSystemState(
            linkedFiles.GetAxiom(),
            (uint)UnityEngine.Random.Range(int.MinValue, int.MaxValue));

        var symbolStringMapping = linkedFiles.allSymbolDefinitionsLeafFirst
                                  .Where(x => x.sourceFileDefinition == "root.lsystem")
                                  .ToDictionary(x => x.actualSymbol, x => x.characterInSourceFile);

        using var axiom = linkedFiles.GetAxiom();
        Assert.AreEqual(axiom.ToString(symbolStringMapping), state.currentSymbols.Data.ToString(symbolStringMapping));
        try
        {
            state = system.StepSystem(state);
            Assert.AreEqual("n(0.5, 0, 10)Fn(0.5, 4, 10)Fn(0.5, 4, 10)", state.currentSymbols.Data.ToString(symbolStringMapping));

            system.customSymbols.diffusionConstantRuntimeGlobalMultiplier = 0.5f;
            state = system.StepSystem(state);
            Assert.AreEqual("n(0.5, 1, 10)Fn(0.5, 3, 10)Fn(0.5, 4, 10)", state.currentSymbols.Data.ToString(symbolStringMapping));

            state = system.StepSystem(state);
            Assert.AreEqual("n(0.5, 1.5, 10)Fn(0.5, 2.75, 10)Fn(0.5, 3.75, 10)", state.currentSymbols.Data.ToString(symbolStringMapping));

            system.customSymbols.diffusionConstantRuntimeGlobalMultiplier = 1f;
            state = system.StepSystem(state);
            Assert.AreEqual("n(0.5, 2.125, 10)Fn(0.5, 2.625, 10)Fn(0.5, 3.25, 10)", state.currentSymbols.Data.ToString(symbolStringMapping));
        }
        finally
        {
            state.currentSymbols.DisposeImmediate();
        }
    }
    public void LinksSimpleDependencyAndAssociatesGlobals()
    {
        var fileSystem = new InMemoryFileProvider();

        fileSystem.RegisterFileWithIdentifier("root.lsystem", @"
#axiom Y
#iterations 1000
#symbols XYZDE

#global DE

#include lib.lsyslib (Exported->X)
Y -> ZX
");

        fileSystem.RegisterFileWithIdentifier("lib.lsyslib", @"
#symbols ABDE
#export Exported B

#global D

B -> AB
");
        var linker    = new FileLinker(fileSystem);
        var linkedSet = linker.LinkFiles("root.lsystem");

        var rootFile = linkedSet.allFiles.data[linkedSet.fileIndexesByFullIdentifier["root.lsystem"]];

        Assert.AreEqual(7, rootFile.allSymbolAssignments.Count);
        Assert.AreEqual(7, rootFile.allSymbolAssignments.Select(x => x.sourceCharacter).Intersect("XYZDE[]").Count());

        var libFile = linkedSet.allFiles.data[linkedSet.fileIndexesByFullIdentifier["lib.lsyslib"]];

        Assert.AreEqual(6, libFile.allSymbolAssignments.Count);
        Assert.AreEqual(6, libFile.allSymbolAssignments.Select(x => x.sourceCharacter).Intersect("ABDE[]").Count());

        var importedInRoot = rootFile.GetSymbolInFile('X');
        var exportedInLib  = libFile.GetSymbolInFile('B');

        Assert.AreEqual(exportedInLib, importedInRoot);

        Assert.AreEqual(rootFile.GetSymbolInFile('D'), libFile.GetSymbolInFile('D'));
        Assert.AreNotEqual(rootFile.GetSymbolInFile('E'), libFile.GetSymbolInFile('E'));

        Assert.AreEqual(rootFile.GetSymbolInFile('['), libFile.GetSymbolInFile('['));
        Assert.AreEqual(rootFile.GetSymbolInFile(']'), libFile.GetSymbolInFile(']'));
    }
    public void CatchesLibraryAsOriginError()
    {
        var fileSystem = new InMemoryFileProvider();

        fileSystem.RegisterFileWithIdentifier("root.lsyslib", @"
#symbols AB
#export Exported A
");
        var linker = new FileLinker(fileSystem);

        try
        {
            linker.LinkFiles("root.lsyslib");
        }
        catch (LinkException e)
        {
            Assert.AreEqual(LinkExceptionType.BASE_FILE_IS_LIBRARY, e.exceptionType);
            return;
        }
        Assert.Fail("linker must not allow the root file to be a library file");
    }
    public void CatchesSimpleCycle()
    {
        var fileSystem = new InMemoryFileProvider();

        fileSystem.RegisterFileWithIdentifier("root.lsystem", @"
#axiom Y
#iterations 1000
#symbols XYZ

#include lib0.lsyslib (Exported->X)
");

        fileSystem.RegisterFileWithIdentifier("lib0.lsyslib", @"
#symbols AB
#include lib1.lsyslib (Exported->B)
#export Exported A
");
        fileSystem.RegisterFileWithIdentifier("lib1.lsyslib", @"
#symbols CD
#include lib2.lsyslib (Exported->D)
#export Exported C
");;
        fileSystem.RegisterFileWithIdentifier("lib2.lsyslib", @"
#symbols EF
#include lib0.lsyslib (Exported->F)
#export Exported E
");
        var linker = new FileLinker(fileSystem);

        try
        {
            linker.LinkFiles("root.lsystem");
        }
        catch (LinkException e)
        {
            Assert.AreEqual(LinkExceptionType.CYCLIC_DEPENDENCY, e.exceptionType);
            return;
        }
        Assert.Fail("linker must prevent cyclic dependencies");
    }
    public void CatchesSimpleImportDissonance()
    {
        var fileSystem = new InMemoryFileProvider();

        fileSystem.RegisterFileWithIdentifier("root.lsystem", @"
#axiom Y
#iterations 1000
#symbols XY

#include lib0.lsyslib (Exported->X)
#include lib1.lsyslib (Exported->Y)
");

        fileSystem.RegisterFileWithIdentifier("lib0.lsyslib", @"
#symbols AB
#include lib2.lsyslib (Exported->A)
#export Exported A
");
        fileSystem.RegisterFileWithIdentifier("lib1.lsyslib", @"
#symbols CD
#include lib2.lsyslib (Exported->C)
#export Exported C
");
        fileSystem.RegisterFileWithIdentifier("lib2.lsyslib", @"
#symbols EF
#export Exported E
");
        var linker = new FileLinker(fileSystem);

        try
        {
            linker.LinkFiles("root.lsystem");
        }
        catch (LinkException e)
        {
            Assert.AreEqual(LinkExceptionType.IMPORT_DISSONANCE, e.exceptionType);
            return;
        }
        Assert.Fail("linker must prevent importing symbols in such a way that would cause a single symbol to be represented as multiple character");
    }
    public void LinksDependencyWithoutRemapping()
    {
        var fileSystem = new InMemoryFileProvider();

        fileSystem.RegisterFileWithIdentifier("root.lsystem", @"
#axiom Y
#iterations 1000
#symbols XYZ

#include lib.lsyslib
Y -> ZX
");

        fileSystem.RegisterFileWithIdentifier("lib.lsyslib", @"
#symbols AB
#export Exported B

B -> AB
");
        var linker    = new FileLinker(fileSystem);
        var linkedSet = linker.LinkFiles("root.lsystem");

        var rootFile = linkedSet.allFiles.data[linkedSet.fileIndexesByFullIdentifier["root.lsystem"]];

        Assert.AreEqual(5, rootFile.allSymbolAssignments.Count);
        Assert.AreEqual(5, rootFile.allSymbolAssignments.Select(x => x.sourceCharacter).Intersect("XYZ[]").Count());

        var libFile = linkedSet.allFiles.data[linkedSet.fileIndexesByFullIdentifier["lib.lsyslib"]];

        Assert.AreEqual(4, libFile.allSymbolAssignments.Count);
        Assert.AreEqual(4, libFile.allSymbolAssignments.Select(x => x.sourceCharacter).Intersect("AB[]").Count());

        var exportedInLib = libFile.GetSymbolInFile('B');

        Assert.IsFalse(rootFile.allSymbolAssignments.Any(x => x.remappedSymbol == exportedInLib));
    }
    public void LinksDependencyWithContextualMatchesAndIgnoresNonImportedSymbols()
    {
        var fileSystem = new InMemoryFileProvider();

        fileSystem.RegisterFileWithIdentifier("root.lsystem", @"
#axiom Y(0)A(3)
#iterations 10
#symbols XYA
#matches XYA

#include nodes.lsyslib (Node->X) (Root->Y)
A(x) : x > 0 -> I(2)[X(x)]A(x - 1)
A(x) : x <= 0 -> J

#symbols IFJ
#matches IJ
I(x) : x > 0 -> I(x - 1)F
J -> 
I(x) > J -> JI(x)
");

        fileSystem.RegisterFileWithIdentifier("nodes.lsyslib", @"
#symbols AB
#matches AB
#export Node B
#export Root A

A(y) < B(x) : x > 0 -> B(x - 1)
       A(x) > [B(y)] : y > 0 -> A(x + 1)
       A(x) > [B(y)][B(z)] : y > 0 && z > 0 -> A(x + 2)
       A(x) > [B(y)][B(z)][B(a)] : y > 0 && z > 0 && a > 0 -> A(x + 3)
       A(x) > [B(y)][B(z)][B(a)][B(b)] : y > 0 && z > 0 && a > 0 && b > 0 -> A(x + 4)

");
        var linker      = new FileLinker(fileSystem);
        var linkedFiles = linker.LinkFiles("root.lsystem");

        using var system = linkedFiles.CompileSystem();
        LSystemState <float> currentState = new DefaultLSystemState(
            linkedFiles.GetAxiom(),
            (uint)UnityEngine.Random.Range(int.MinValue, int.MaxValue));

        var lBracket = linkedFiles.GetSymbol("root.lsystem", '[');
        var rBracket = linkedFiles.GetSymbol("root.lsystem", ']');
        var XRoot    = linkedFiles.GetSymbol("root.lsystem", 'X');
        var YRoot    = linkedFiles.GetSymbol("root.lsystem", 'Y');
        var ARoot    = linkedFiles.GetSymbol("root.lsystem", 'A');
        var IRoot    = linkedFiles.GetSymbol("root.lsystem", 'I');
        var FRoot    = linkedFiles.GetSymbol("root.lsystem", 'F');
        var JRoot    = linkedFiles.GetSymbol("root.lsystem", 'J');
        var Alib     = linkedFiles.GetSymbol("nodes.lsyslib", 'A');
        var Blib     = linkedFiles.GetSymbol("nodes.lsyslib", 'B');

        Assert.AreEqual(Alib, YRoot);
        Assert.AreEqual(Blib, XRoot);

        var symbolStringMapping = new Dictionary <int, char>()
        {
            { lBracket, '[' },
            { rBracket, ']' },
            { XRoot, 'X' },
            { YRoot, 'Y' },
            { ARoot, 'A' },
            { IRoot, 'I' },
            { FRoot, 'F' },
            { JRoot, 'J' }
        };

        Assert.AreEqual("Y(0)A(3)", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("Y(0)I(2)[X(3)]A(2)", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("Y(1)I(1)F[X(2)]I(2)[X(2)]A(1)", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("Y(3)I(0)FF[X(1)]I(1)F[X(1)]I(2)[X(1)]A(0)", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("Y(6)I(0)FF[X(0)]I(0)FF[X(0)]I(1)F[X(0)]J", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("Y(6)I(0)FF[X(0)]I(0)FF[X(0)]JI(1)F[X(0)]", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("Y(6)I(0)FF[X(0)]JI(0)FF[X(0)]I(0)FF[X(0)]", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("Y(6)JI(0)FF[X(0)]I(0)FF[X(0)]I(0)FF[X(0)]", currentState.currentSymbols.Data.ToString(symbolStringMapping));
        currentState = system.StepSystem(currentState);
        Assert.AreEqual("Y(6)I(0)FF[X(0)]I(0)FF[X(0)]I(0)FF[X(0)]", currentState.currentSymbols.Data.ToString(symbolStringMapping));


        currentState.currentSymbols.DisposeImmediate();
    }