diff --git a/benches/visual/query.bench.luau b/benches/visual/query.bench.luau index 37df156..90426f1 100644 --- a/benches/visual/query.bench.luau +++ b/benches/visual/query.bench.luau @@ -57,7 +57,7 @@ local function flip() end local common = 0 -local N = 500 +local N = 2^16-2 local archetypes = {} local hm = 0 @@ -170,19 +170,19 @@ return { end, Functions = { - Mirror = function() - for i = 1, 1000 do - for entityId, firstComponent in mcs:query(E1, E4) do - end + Matter = function() + for entityId, firstComponent in newWorld:query(A1, A2, A3, A4) do end + end, + ECR = function() + for entityId, firstComponent in registry2:view(B1, B2, B3, B3) do + end end, Jecs = function() - for i = 1, 1000 do - for entityId, firstComponent in ecs:query(D1, D4) do - end - end + for entityId, firstComponent in ecs:query(D1, D2, D3, D4) do + end end, }, } diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index b3917c5..97c93d1 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -35,9 +35,9 @@ export default defineConfig({ { text: 'Concepts', items: [ - { text: 'Entities', link: 'learn/concepts/entities' }, - { text: 'Static Components', link: 'learn/concepts/static-components' }, + { text: 'Entities and Components', link: 'learn/concepts/entities-and-components' }, { text: 'Queries', link: 'learn/concepts/queries' }, + { text: 'Relationships', link: 'learn/concepts/relationships' }, ] }, { @@ -61,7 +61,7 @@ export default defineConfig({ }, socialLinks: [ - { icon: 'github', link: 'https://github.com/vuejs/vitepress' } + { icon: 'github', link: 'https://github.com/ukendio/jecs' } ] } }) diff --git a/docs/api/jecs.md b/docs/api/jecs.md index 61a66a8..cc2ee46 100644 --- a/docs/api/jecs.md +++ b/docs/api/jecs.md @@ -8,10 +8,31 @@ Jecs. Just an Entity Component System. ```luau jecs.World: World ``` +A world is a container of all ECS data. Games can have multiple worlds but component IDs may conflict between worlds. Ensure to register the same component IDs in the same order for each world. ### Wildcard +```luau +jecs.Wildcard: Entity +``` +Builtin component type. This ID is used for wildcard queries. -### z<> +### Component +```luau +jecs.Component: Entity +``` +Builtin component type. Every ID created with [world:component()](world.md#component()) has this type added to it. This is meant for querying every component ID. + +### ChildOf +```luau +jecs.ChildOf: Entity +``` +Builtin component type. This ID is for creating parent-child hierarchies. + +::: +### Rest +```luau +jecs.Rest: Entity +``` ## Functions diff --git a/docs/api/query.md b/docs/api/query.md index e7702fa..da66397 100644 --- a/docs/api/query.md +++ b/docs/api/query.md @@ -4,129 +4,128 @@ A World contains entities which have components. The World is queryable and can ## Functions -### new() +### drain() ```luau -function World.new(): World +function query:drain(): Query ``` -Creates a new world. +This function will impede it from being reset when the query is being iterated. + +### next() +```luau +function query:next(): Query +``` +Get the next result in the query. Drain must have been called beforehand or otherwise it will error. + +### with() +```luau +function query:with( + ...: Entity -- The IDs to query with +): Query +``` +Adds IDs to query with, but will not use their data. This is useful for Tags or generally just data you do not care for. Example: ::: code-group ```luau [luau] -local world = jecs.World.new() -``` - -```ts [typescript] -import { World } from "@rbxts/jecs"; - -const world = new World(); -``` - -::: - -## entity() -```luau -function World:entity(): Entity -- The new entit. -``` -Creates a new entity. - -Example: -::: code-group - -```luau [luau] -local entity = world:entity() -``` - -```ts [typescript] -const entity = world.entity(); -``` - -:: -: - -### component() -```luau -function World:component(): Entity -- The new componen. -``` -Creates a new component. - -Example: -::: code-group - -```luau [luau] -local Health = world:component() :: jecs.Entity -``` - -```ts [typescript] -const Health = world.component(); -``` -::: - -::: info -You should use this when creating components. - -For example, a Health type should be created using this. -::: - -### get() -```luau -function World:get( - entity: Entity, -- The entity - ...: Entity -- The types to fetch -): ... -- Returns the component data in the same order they were passed in -``` -Returns the data for each provided type for the corresponding entity. - -::: - -### add() -```luau -function World:add( - entity: Entity, -- The entity - id: Entity -- The component ID to add -): () -``` -Adds a component ID to the entity. - -This operation adds a single (component) id to an entity. - -::: info -This function is idempotent, meaning if the entity already has the id, this operation will have no side effects. -::: - - -### set() -```luau -function World:set( - entity: Entity, -- The entity - id: Entity, -- The component ID to set - data: T -- The data of the component's type -): () -``` -Adds or changes the entity's component. - -### query() -```luau -function World:query( - ...: Entity -- The component IDs to query with. Entities that satifies the conditions will be returned -): Query<...Entity> -- Returns the Query which gets the entity and their corresponding data when iterated -``` -Creates a [`query`](query) with the given component IDs. - -Example: -::: code-group - -```luau [luau] -for id, position, velocity in world:query(Position, Velocity) do - -- Do something +for id, position in world:query(Position):with(Velocity) do + -- Do something end ``` ```ts [typescript] -for (const [id, position, velocity] of world.query(Position, Velocity) { +for (const [id, position] of world.query(Position).with(Velocity)) { // Do something } ``` ::: + +:::info +Put the IDs inside of `world:query()` instead if you need the data. +::: + +### without() + +```luau +function query:without( + ...: Entity -- The IDs to filter against. +): Query -- Returns the Query +``` +Removes entities with the provided IDs from the query. + +Example: +::: code-group + +```luau [luau] +for _ in world:query(Position):without(Velocity) do + -- Do something +end +``` + +```ts [typescript] +for (const _ of world.query(Position).without(Velocity)) { + // Do something +} +``` + +::: + +### replace() + +```luau +function query:replace( + fn: (entity: Entity, ...: T...) -> U... -- ): () -- The callback that will transform the entities' data +``` +This function takes a callback which is given the current queried data of each matching entity. The values returned by the callback will be set as the new data for each given ID on the entity. + +Example: +::: code-group + +```luau [luau] +world:query(Position, Velocity):replace(function(e, position, velocity) + return position + velocity, velocity * 0.9 +end +``` + +```ts [typescript] +world + .query(Position, Velocity) + .replace((e, position, velocity) => + $tuple(position.add(velocity), velocity.mul(0.9)), + ); +``` + +::: + + +### archetypes() +```luau +function query.archetypes(): { Archetype } +``` +Returns the matching archetypes of the query. + +Example: +::: code-group + +```luau [luau] +for i, archetype in world:query(Position, Velocity).archetypes() do + local columns = archetype.columns + local field = archetype.records + + local P = field[Position] + local V = field[Velocity] + + for row, entity in archetype.entities do + local position = columns[P][row] + local velocity = columns[V][row] + -- Do something + end +end +``` + +::: + +:::info +This function is meant for internal usage. Use this if you want to maximize performance by inlining the iterator. +::: diff --git a/docs/api/world.md b/docs/api/world.md index e7702fa..ed2edbd 100644 --- a/docs/api/world.md +++ b/docs/api/world.md @@ -1,4 +1,4 @@ -# Query +# World A World contains entities which have components. The World is queryable and can be used to get entities with a specific set of components. @@ -109,10 +109,10 @@ Adds or changes the entity's component. ### query() ```luau function World:query( - ...: Entity -- The component IDs to query with. Entities that satifies the conditions will be returned -): Query<...Entity> -- Returns the Query which gets the entity and their corresponding data when iterated + ...: Entity -- The IDs to query with +): Query -- Returns the Query ``` -Creates a [`query`](query) with the given component IDs. +Creates a [`query`](query) with the given IDs. Entities that satisfies the conditions of the query will be returned and their corresponding data. Example: ::: code-group @@ -130,3 +130,36 @@ for (const [id, position, velocity] of world.query(Position, Velocity) { ``` ::: + +:::info +Queries are uncached by default, this is generally very cheap unless you have high fragmentation from e.g. relationships. + +::: +### target() +```luau +function World:target( + entity: Entity, -- The entity + relation: Entity -- The relationship between the entity and the target +): Entity? -- Returns the parent of the child +``` + +Get the target of a relationship. + +This will return a target (second element of a pair) of the entity for the specified relationship. + +If there is no pair with specified relationship, it will return nil. + +### parent() +```luau +function World:parent( + child: Entity -- The child ID to find the parent of +): Entity? -- Returns the parent of the child +``` + +Get parent (target of ChildOf relationship) for entity. If there is no ChildOf relationship pair, it will return nil. + +This operation is the same as calling: + +```luau +world:target(entity, jecs.ChildOf) +``` diff --git a/docs/learn/concepts/entities-and-components.md b/docs/learn/concepts/entities-and-components.md new file mode 100644 index 0000000..42087e0 --- /dev/null +++ b/docs/learn/concepts/entities-and-components.md @@ -0,0 +1,115 @@ +# Entities and Components + +## Entities +Entities represent things in a game. In a game there may be entities of characters, buildings, projectiles, particle effects etc. + +By itself, an entity is just an unique identifier without any data + +### Creation + +## Components +A component is something that is added to an entity. Components can simply tag an entity ("this entity is an `Npc`"), attach data to an entity ("this entity is at `Position` `Vector3.new(10, 20, 30)`") and create relationships between entities ("bob `Likes` alice") that may also contain data ("bob `Eats` `10` apples"). + +### Operations +Operation | Description +----------|------------ +`get` | Get a specific component or set of components from an entity. +`add` | Adds component to an entity. If entity already has the component, `add` does nothing. +`set` | Sets the value of a component for an entity. `set` behaves as a combination of `add` and `get` +`remove` | Removes component from entity. If entity doesn't have the component, `remove` does nothing. +`clear` | Remove all components from an entity. Clearing is more efficient than removing one by one. + +### Components are entities + +In an ECS, components need to be uniquely identified. In Jecs this is done by making each component is its own unique entity. If a game has a component Position and Velocity, there will be two entities, one for each component. Component entities can be distinguished from "regular" entities as they have a `Component` component. An example: + +::: code-group + +```luau [luau] +local Position = world:component() :: jecs.Entity +print(world:has(Position, Jecs.Component)) +``` + +```typescript [typescript] +const Position = world.component(); +print(world.has(Position, Jecs.Component)) +``` + +::: + +All of the APIs that apply to regular entities also apply to component entities. This means it is possible to contexualize components with logic by adding traits to components + +::: code-group + +```luau [luau] +local Networked = world:component() +local Type = world:component() +local Name = world:component() +local Position = world:component() :: jecs.Entity +world:add(Position, Networked) +world:set(Posiition, Name, "Position") +world:set(Position, Type, { size = 12, type = "Vector3" } ) -- 12 bytes to represent a Vector3 + +for id, ty, name in world:query(Type, Name):with(Networked) do + local batch = {} + for entity, data in world:query(id) do + table.insert(batch, { entity = entity, data = data }) + end + -- entities are sized f64 + local packet = buffer.create(#batch * (8 + ty.size)) + local offset = 0 + for _, entityData in batch do + offset+=8 + buffer.writef64(packet, offset, entityData.entity) + if ty.type == "Vector3" then + local vec3 = entity.data :: Vector3 + offset += 4 + buffer.writei32(packet, offset, vec3.X) + offset += 4 + buffer.writei32(packet, offset, vec3.Y) + offset += 4 + buffer.writei32(packet, offset, vec3.Z) + end + end + + updatePositions:FireServer(packet) +end +``` + +```typescript [typescript] +const Networked = world.component() +const Type = world.component() +const Name = world.component() +const Position = world.component(); +world.add(Position, Networked) +world.set(Posiition, Name, "Position") +world.set(Position, Type, { size: 12, type: "Vector3" } ) // 12 bytes to represent a Vector3 + +for (const [id, ty, name] of world.query(Type, Name).with(Networked)) { + const batch = new Array<{ entity: Entity, data: unknown}>() + + for (const [entity, data] of world.query(id)) { + batch.push({ entity, data }) + } + // entities are sized f64 + const packet = buffer.create(batch.size() * (8 + ty.size)) + const offset = 0 + for (const [_, entityData] of batch) { + offset+=8 + buffer.writef64(packet, offset, entityData.entity) + if (ty.type == "Vector3") { + const vec3 = entity.data as Vector3 + offset += 4 + buffer.writei32(packet, offsetm, vec3.X) + offset += 4 + buffer.writei32(packet, offset, vec3.Y) + offset += 4 + buffer.writei32(packet, offset, vec3.Z) + } + } + + updatePositions.FireServer(packet) +} +``` + +::: diff --git a/docs/learn/concepts/entities.md b/docs/learn/concepts/entities.md deleted file mode 100644 index 8b13789..0000000 --- a/docs/learn/concepts/entities.md +++ /dev/null @@ -1 +0,0 @@ - diff --git a/docs/learn/concepts/queries.md b/docs/learn/concepts/queries.md index 1346d9f..733e833 100644 --- a/docs/learn/concepts/queries.md +++ b/docs/learn/concepts/queries.md @@ -1,3 +1,104 @@ -## TODO +# Queries -This is a TODO stub. \ No newline at end of file +## Introductiuon + +Queries enable games to quickly find entities that satifies provided conditions. + +Jecs queries can do anything from returning entities that match a simple list of components, to matching against entity graphs. + +This manual contains a full overview of the query features available in Jecs. Some of the features of Jecs queries are: + +- Queries have support for relationships pairs which allow for matching against entity graphs without having to build complex data structures for it. +- Queries support filters such as `query:with(...)` if entities are required to have the components but you don’t actually care about components value. And `query:without(...)` which selects entities without the components. +- Queries can be drained or reset on when called, which lets you choose iterator behaviour. +- Queries can be called with any ID, including entities created dynamically, this is useful for pairs. +- Queries are already fast but can be futher inlined via `query:archetypes()` for maximum performance to eliminate function call overhead which is roughly 70-80% of the cost for iteration. + +## Creating Queries +This section explains how to create queries in the different language bindings. + +:::code-group +```luau [luau] +for _ in world:query(Position, Velocity) do end +``` +```typescript [typescript] +for (const [_] of world.query(Position, Velocity)) {} +``` +::: + +### Components +A component is any single ID that can be added to an entity. This includes tags and regular entities, which are IDs that do not have the builtin `Component` component. To match a query, an entity must have all the requested components. An example: + +```luau +local e1 = world:entity() +world:add(e1, Position) + +local e2 = world:entity() +world:add(e2, Position) +world:add(e2, Velocity) + +local e3 = world:entity() +world:add(e3, Position) +world:add(e3, Velocity) +world:add(e3, Mass) + +``` +Only entities `e2` and `e3` match the query Position, Velocity. + +### Wildcards + +Jecs currently only supports the `Any` type of wildcards which a single result for the first component that it matches. + +When using the `Any` type wildcard it is undefined which component will be matched, as this can be influenced by other parts of the query. It is guaranteed that iterating the same query twice on the same dataset will produce the same result. + +Wildcards are particularly useful when used in combination with pairs (next section). + +### Pairs + +A pair is an ID that encodes two elements. Pairs, like components, can be added to entities and are the foundation for [Relationships](relationships.md). + +The elements of a pair are allowed to be wildcards. When a query pair returns an `Any` type wildcard, the query returns at most a single matching pair on an entity. + +The following sections describe how to create queries for pairs in the different language bindings. + +:::code-group +```luau [luau] +local Likes = world:entity() +local bob = world:entity() +for _ in world:query(pair(Likes, bob)) do end +``` +```typescript [typescript] +const Likes = world.entity() +const bob = world.entity() +for (const [_] of world.query(pair(Likes, bob))) {} +``` +::: + +When a query pair contains a wildcard, the `world:target()` function can be used to determine the target of the pair element that matched the query: + +:::code-group +```luau [luau] +for id in world:query(pair(Likes, jecs.Wildcard)) do + print(`entity {getName(id)} likes {getName(world, world:target(id, Likes))}`) +end +``` +```typescript [typescript] +const Likes = world.entity() +const bob = world.entity() +for (const [_] of world.query(pair(Likes, jecs.Wildcard))) { + print(`entity ${getName(id)} likes ${getName(world.target(id, Likes))}`) +} +``` +::: + +### Filters +Filters are extensions to queries which allow you to select entities from a more complex pattern but you don't actually care about the component values. + +The following filters are supported by queries: + +Identifier | Description +---------- | ----------- +With | Must match with all terms. +Without | Must not match with provided terms. + +This page takes wording and terminology directly from Flecs [documentation](https://www.flecs.dev/flecs/md_docs_2Queries.html) diff --git a/docs/learn/concepts/relationships.md b/docs/learn/concepts/relationships.md new file mode 100644 index 0000000..2ac8a89 --- /dev/null +++ b/docs/learn/concepts/relationships.md @@ -0,0 +1,210 @@ +# Relationships +Relationships makes it possible to describe entity graphs natively in ECS. + +Adding/removing relationships is similar to adding/removing regular components, with as difference that instead of a single component id, a relationship adds a pair of two things to an entity. In this pair, the first element represents the relationship (e.g. "Eats"), and the second element represents the relationship target (e.g. "Apples"). + +Relationships can be used to describe many things, from hierarchies to inventory systems to trade relationships between players in a game. The following sections go over how to use relationships, and what features they support. + +## Definitions + +Name | Description +----------|------------ +Id | An id that can be added and removed +Component | Id with a single element (same as an entity id) +Relationship | Used to refer to first element of a pair +Target | Used to refer to second element of a pair +Source | Entity to which an id is added + +## Relationship queries +There are a number of ways a game can query for relationships. The following kinds of queries are available for all (unidirectional) relationships, and are all constant time: + +Test if entity has a relationship pair + +:::code-group +```luau [luau] +world:has(bob, pair(Eats, Apples) +``` +```typescript [typescript] +world.has(bob, pair(Eats, Apples) +``` +::: + +Test if entity has a relationship wildcard + +:::code-group +```luau [luau] +world:has(bob, pair(Eats, jecs.Wildcard) +``` +```typescript [typescript] +world.has(bob, pair(Eats, jecs.Wildcard) +``` +::: + +Get parent for entity + +:::code-group +```luau [luau] +world:parent(bob) +``` +```typescript [typescript] +world.parent(bob, pair(Eats, jecs.Wildcard) +``` +::: + +Find first target of a relationship for entity + +:::code-group +```luau [luau] +world:target(bob, Eats) +``` +```typescript [typescript] +world.target(bob, Eats) +``` +::: + + +Find first target of a relationship for entity + +:::code-group +```luau [luau] +world:target(bob, Eats) +``` +```typescript [typescript] +world.target(bob, Eats) +``` +::: + +Find all entities with a pair + +:::code-group +```luau [luau] +for id in world:query(pair(Eats, Apples)) do + -- ... +end +``` +```typescript [typescript] +for (const [id] of world.query(pair(Eats, Apples)) { + // ... +} +``` +::: + +Find all entities with a pair wildcard + +:::code-group +```luau [luau] +for id in world:query(pair(Eats, jecs.Wildcard)) do + local food = world:target(id, Eats) -- Apples, ... +end +``` +```typescript [typescript] +for (const [id] of world.query(pair(Eats, jecs.Wildcard)) { + const food = world.target(id, Eats) // Apples, ... +} +``` +::: + +Iterate all children for a parent + +:::code-group +```luau [luau] +for child in world:query(pair(jecs.ChildOf, parent)) do + -- ... +end +``` +```typescript [typescript] +for (const [child] of world.query(pair(jecs.ChildOf, parent)) { + // ... +} +``` +::: + +Relationship components + +Relationship pairs, just like regular component, can be associated with data. + +:::code-group +```luau [luau] +local Position = world:component() +local Eats = world:component() +local Apples = world:entity() +local Begin = world:entity() +local End = world:entity() + +local e = world:entity() +world:set(e, pair(Eats, Apples), { amount = 1 }) + +world:set(e, pair(Begin, Position), Vector3.new(0, 0, 0)) +world:set(e, pair(End, Position), Vector3.new(10, 20, 30)) + +world:add(e, jecs.ChildOf, Position) + +``` +```typescript [typescript] +const Position = world.component() +const Eats = world.component() +const Apples = world.entity() +const Begin = world.entity() +const End = world.entity() + +const e = world.entity() +world.set(e, pair(Eats, Apples), { amount: 1 }) + +world.set(e, pair(Begin, Position), new Vector3(0, 0, 0)) +world.set(e, pair(End, Position), new Vector3(10, 20, 30)) + +world.add(e, jecs.ChildOf, Position) +``` +::: + +## Relationship wildcards + +When querying for relationship pairs, it is often useful to be able to find all instances for a given relationship or target. To accomplish this, an game can use wildcard expressions. + +Wildcards may used for the relationship or target part of a pair + +```luau +pair(Likes, jecs.Wildcard) -- Matches all Likes relationships +pair(jecs.Wildcard, Alice) -- Matches all relationships with Alice as target +``` + +## Relationship performance +This section goes over the performance implications of using relationships. + +### Introduction +The ECS storage needs to know two things in order to store components for entities: +- Which IDs are associated with an entity +- Which types are associated with those ids +Ids represent anything that can be added to an entity. An ID that is not associated with a type is called a tag. An ID associated with a type is a component. For regular components, the ID is a regular entity that has the builtin `Component` component. + +### Storing relationships +Relationships do not fundamentally change or extend the capabilities of the storage. Relationship pairs are two elements encoded into a single 53-bit ID, which means that on the storage level they are treated the same way as regular component IDs. What changes is the function that determines which type is associated with an id. For regular components this is simply a check on whether an entity has `Component`. To support relationships, new rules are added to determine the type of an id. + +Because of this, adding/removing relationships to entities has the same performance as adding/removing regular components. This becomes more obvious when looking more closely at a function that adds a relationship pair. + +### Id ranges +Jecs reserves entity ids under a threshold (HI_COMPONENT_ID, default is 256) for components. This low id range is used by the storage to more efficiently encode graph edges between archetypes. Graph edges for components with low ids use direct array indexing, whereas graph edges for high ids use a hashmap. Graph edges are used to find the next archetype when adding/removing component ids, and are a contributing factor to the performance overhead of add/remove operations. + +Because of the way pair IDs are encoded, a pair will never be in the low id range. This means that adding/removing a pair ID always uses a hashmap to find the next archetype. This introduces a small overhead. + +### Fragmentation +Fragmentation is a property of archetype-based ECS implementations where entities are spread out over more archetypes as the number of different component combinations increases. The overhead of fragmentation is visible in two areas: +- Archetype creation +- ueries (queries have to match & iterate more archetypes) +Games that make extensive use of relationships might observe high levels of fragmentation, as relationships can introduce many different combinations of components. While the Jecs storage is optimized for supporting large amounts (hundreds of thousands) of archetypes, fragmentation is a factor to consider when using relationships. + +Union relationships are planned along with other improvements to decrease the overhead of fragmentation introduced by relationships. + +### Archetype Creation + +When an ID added to an entity is deleted, all references to that ID are deleted from the storage. For example, when the component Position is deleted it is removed from all entities, and all archetypes with the Position component are deleted. While not unique to relationships, it is more common for relationships to trigger cleanup actions, as relationship pairs contain regular entities. + +The opposite is also true. Because relationship pairs can contain regular entities which can be created on the fly, archetype creation is more common than in games that do not use relationships. While Jecs is optimized for fast archetypes creation, creating and cleaning up archetypes is inherently more expensive than creating/deleting an entity. Therefore archetypes creation is a factor to consider, especially for games that make extensive use of relationships. + +### Indexing + +To improve the speed of evaluating queries, Jecs has indices that store all archetypes for a given component ID. Whenever a new archetype is created, it is registered with the indices for the IDs the archetype has, including IDs for relationship pairs. + +While registering a archetype for a relationship index is not more expensive than registering a archetype for a regular index, a archetype with relationships has to also register itself with the appropriate wildcard indices for its relationships. For example, an archetype with relationship `pair(Likes, Apples)` registers itself with the `pair(Likes, Apples)`, `pair(Likes, jecs.Wildcard)` and `pair(jecs.Wildcard, Apples)` indices. For this reason, creating new archetypes with relationships has a higher overhead than a archetype without relationships. + +This page takes wording and terminology directly from Flecs, the first ECS with full support for [Entity Relationships](https://www.flecs.dev/flecs/md_docs_2Relationships.html). diff --git a/docs/learn/concepts/static-components.md b/docs/learn/concepts/static-components.md deleted file mode 100644 index 1346d9f..0000000 --- a/docs/learn/concepts/static-components.md +++ /dev/null @@ -1,3 +0,0 @@ -## TODO - -This is a TODO stub. \ No newline at end of file diff --git a/docs/learn/overview/first-jecs-project.md b/docs/learn/overview/first-jecs-project.md index 1346d9f..ea79cf2 100644 --- a/docs/learn/overview/first-jecs-project.md +++ b/docs/learn/overview/first-jecs-project.md @@ -1,3 +1,71 @@ -## TODO +# First Jecs project -This is a TODO stub. \ No newline at end of file +Now that you have installed Jecs, you can create your [World](https://ukendio.github.io/jecs/api/world.html). + +:::code-group +```luau [luau] +local jecs = require(path/to/jecs) +local world = jecs.World.new() +``` +```typescript [typescript] +import { World } from "@rbxts/jecs" +const world = new World() +``` +::: + +Let's create a couple components. + +:::code-group +```luau [luau] +local jecs = require(path/to/jecs) +local world = jecs.World.new() + +local Position = world:component() +local Velocity = world:component() +``` + +```typescript [typescript] +import { World } from "@rbxts/jecs" +const world = new World() + +const Position = world.component() +const Velocity = world.component() +``` +::: + +Systems can be as simple as a query in a function or a more contextualized construct. Let's make a system that moves an entity and decelerates over time. + +:::code-group +```luau [luau] +local jecs = require(path/to/jecs) +local world = jecs.World.new() + +local Position = world:component() +local Velocity = world:component() + +for id, position, velocity in world:query(Position, Velocity) do + world:set(id, Position, position += velocity) + world:set(id, Velocity, velocity * 0.9) +end +``` + +```typescript [typescript] +import { World } from "@rbxts/jecs" +const world = new World() + +const Position = world.component() +const Velocity = world.component() + +for (const [id, position, velocity] of world.query(Position, Velocity)) { + world.set(id, Position, position.add(velocity) + world.set(id, Velocity, velocity.mul(0.9)) +} +``` +::: + +## Where To Get Help + +If you are encounting problems, there are resources for you to get help: +- [Roblox OSS Discord server](https://discord.gg/h2NV8PqhAD) has a [#jecs](https://discord.com/channels/385151591524597761/1248734074940559511) thread under the [#projects](https://discord.com/channels/385151591524597761/1019724676265676930) channel +- [Open an issue](https://github.com/ukendio/jecs/issues) if you run into bugs or have feature requests +- Dive into the nitty gritty in the [thesis paper](https://raw.githubusercontent.com/Ukendio/jecs/main/thesis/drafts/1/paper.pdf) diff --git a/docs/public/jecs_logo.svg b/docs/public/jecs_logo.svg new file mode 100644 index 0000000..befe822 --- /dev/null +++ b/docs/public/jecs_logo.svg @@ -0,0 +1,41 @@ + + + +Created with Fabric.js 5.2.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/image-3.png b/image-3.png index 1039142..0324903 100644 Binary files a/image-3.png and b/image-3.png differ diff --git a/image.png b/image.png deleted file mode 100644 index d6497ff..0000000 Binary files a/image.png and /dev/null differ diff --git a/src/init.luau b/src/init.luau index 9b16140..efedb7b 100644 --- a/src/init.luau +++ b/src/init.luau @@ -24,7 +24,7 @@ type Archetype = { type: string | number, entities: { number }, columns: { Column }, - records: { [number]: number }, + records: { ArchetypeRecord }, } type Record = { archetype: Archetype, @@ -35,17 +35,11 @@ type Record = { type EntityIndex = { dense: { [i24]: i53 }, sparse: { [i53]: Record } } -type ArchetypeRecord = number ---[[ -TODO: -{ - index: number, +type ArchetypeRecord = { count: number, column: number } -]] - type ArchetypeMap = { cache: { ArchetypeRecord }, first: ArchetypeMap, @@ -191,17 +185,17 @@ local function archetype_move(entityIndex: EntityIndex, to: Archetype, local sourceEntities = from.entities local destinationEntities = to.entities local destinationColumns = to.columns - local tr = to.records + local records = to.records local types = from.types for i, column in columns do -- Retrieves the new column index from the source archetype's record from each component -- We have to do this because the columns are tightly packed and indexes may not correspond to each other. - local targetColumn = destinationColumns[tr[types[i]]] + local tr = records[types[i]] -- Sometimes target column may not exist, e.g. when you remove a component. - if targetColumn then - targetColumn[destinationRow] = column[sourceRow] + if tr then + destinationColumns[tr.column][destinationRow] = column[sourceRow] end -- If the entity is the last row in the archetype then swapping it would be meaningless. local last = #column @@ -283,7 +277,7 @@ local function ECS_ID_IS_WILDCARD(e: i53): boolean return first == EcsWildcard or second == EcsWildcard end -local function archetype_of(world: any, types: { i24 }, prev: Archetype?): Archetype +local function archetype_create(world: any, types: { i24 }, prev: Archetype?): Archetype local ty = hash(types) local id = world.nextArchetypeId + 1 @@ -293,12 +287,13 @@ local function archetype_of(world: any, types: { i24 }, prev: Archetype?): Arche local columns = (table.create(length) :: any) :: { Column } local componentIndex = world.componentIndex - local records = {} + local records: { ArchetypeRecord } = {} for i, componentId in types do + local tr = { column = i, count = 1 } local idr = id_record_ensure(componentIndex, componentId) - idr.cache[id] = i + idr.cache[id] = tr idr.size += 1 - records[componentId] = i + records[componentId] = tr if ECS_IS_PAIR(componentId) then local relation = ecs_pair_relation(world.entityIndex, componentId) local object = ecs_pair_object(world.entityIndex, componentId) @@ -309,11 +304,11 @@ local function archetype_of(world: any, types: { i24 }, prev: Archetype?): Arche local o = ECS_PAIR(EcsWildcard, object) local idr_o = id_record_ensure(componentIndex, o) - records[r] = i - records[o] = i + records[r] = tr + records[o] = tr - idr_r.cache[id] = i - idr_o.cache[id] = i + idr_r.cache[id] = tr + idr_o.cache[id] = tr idr_r.size += 1 idr_o.size += 1 @@ -375,7 +370,7 @@ local function world_target(world: World, entity: i53, relation: i24--[[, nth: n return nil end - return ecs_pair_object(entityIndex, archetype.types[archetypeRecord]) + return ecs_pair_object(entityIndex, archetype.types[archetypeRecord.column]) end local function world_parent(world: World, entity: i53) @@ -393,7 +388,7 @@ local function archetype_ensure(world: World, types, prev): Archetype return archetype end - return archetype_of(world, types, prev) + return archetype_create(world, types, prev) end local function find_insert(types: { i53 }, toAdd: i53): number @@ -478,7 +473,7 @@ local function world_set(world: World, entityId: i53, componentId: i53, data: un -- If the archetypes are the same it can avoid moving the entity -- and just set the data directly. local archetypeRecord = to.records[componentId] - from.columns[archetypeRecord][record.row] = data + from.columns[archetypeRecord.column][record.row] = data -- Should fire an OnSet event here. return end @@ -494,7 +489,7 @@ local function world_set(world: World, entityId: i53, componentId: i53, data: un end local archetypeRecord = to.records[componentId] - to.columns[archetypeRecord][record.row] = data + to.columns[archetypeRecord.column][record.row] = data end local function world_component(world: World): i53 @@ -630,14 +625,14 @@ end local world_get: (world: World, entityId: i53, a: i53, b: i53?, c: i53?, d: i53?, e: i53?) -> (...any) do -- Keeping the function as small as possible to enable inlining - local function fetch(id: i24, records, columns, row): any + local function fetch(id: i24, records: { ArchetypeRecord }, columns, row): any local tr = records[id] if not tr then return nil end - return columns[tr][row] + return columns[tr.column][row] end function world_get(world: World, entityId: i53, a: i53, b: i53?, c: i53?, d: i53?, e: i53?): ...any @@ -720,15 +715,18 @@ do return nil :: any end + local Arm = function(self: Query, ...) + return self + end local EmptyQuery: Query = { __iter = function(): Item return noop end, + drain = Arm, next = noop :: Item, replace = noop :: (Query, ...any) -> (), - without = function(self: Query, ...) - return self - end + with = Arm, + without = Arm, } setmetatable(EmptyQuery, EmptyQuery) @@ -736,121 +734,297 @@ do local lastArchetype: number local archetype: Archetype local queryOutput: { any } - local queryLength: number local entities: { number } local i: number local compatible_archetypes: { Archetype } - local column_indices: { { number} } local ids: { number } + local columns - local function world_query_next(): any - local entityId = entities[i] - while entityId == nil do - lastArchetype += 1 - archetype = compatible_archetypes[lastArchetype] - if not archetype then - return nil - end - entities = archetype.entities - i = #entities - entityId = entities[i] - end + local A, B, C, D, E, F, G, H, I + local a, b, c, d, e, f, g, h - local row = i - i-=1 + local init + local drain - local columns = archetype.columns - local tr = column_indices[lastArchetype] + local function query_init(query) + if init and drain then + return + end - if queryLength == 1 then - return entityId, columns[tr[1]][row] - elseif queryLength == 2 then - return entityId, columns[tr[1]][row], columns[tr[2]][row] - elseif queryLength == 3 then - return entityId, columns[tr[1]][row], columns[tr[2]][row], columns[tr[3]][row] - elseif queryLength == 4 then - return entityId, columns[tr[1]][row], columns[tr[2]][row], columns[tr[3]][row], columns[tr[4]][row] - elseif queryLength == 5 then - return entityId, - columns[tr[1]][row], - columns[tr[2]][row], - columns[tr[3]][row], - columns[tr[4]][row], - columns[tr[5]][row] - elseif queryLength == 6 then - return entityId, - columns[tr[1]][row], - columns[tr[2]][row], - columns[tr[3]][row], - columns[tr[4]][row], - columns[tr[5]][row], - columns[tr[6]][row] - elseif queryLength == 7 then - return entityId, - columns[tr[1]][row], - columns[tr[2]][row], - columns[tr[3]][row], - columns[tr[4]][row], - columns[tr[5]][row], - columns[tr[6]][row], - columns[tr[7]][row] - elseif queryLength == 8 then - return entityId, - columns[tr[1]][row], - columns[tr[2]][row], - columns[tr[3]][row], - columns[tr[4]][row], - columns[tr[5]][row], - columns[tr[6]][row], - columns[tr[7]][row], - columns[tr[8]][row] - end + init = true + lastArchetype = 1 + archetype = compatible_archetypes[lastArchetype] - for j in ids do - queryOutput[j] = columns[tr[j]][row] - end + if not archetype then + return + end - return entityId, unpack(queryOutput, 1, queryLength) + queryOutput = {} + + entities = archetype.entities + i = #entities + columns = archetype.columns + + local records = archetype.records + if not B then + a = columns[records[A].column] + elseif not C then + a = columns[records[A].column] + b = columns[records[B].column] + elseif not D then + a = columns[records[A].column] + b = columns[records[B].column] + c = columns[records[C].column] + elseif not E then + a = columns[records[A].column] + b = columns[records[B].column] + c = columns[records[C].column] + d = columns[records[D].column] + elseif not F then + a = columns[records[A].column] + b = columns[records[B].column] + c = columns[records[C].column] + d = columns[records[D].column] + e = columns[records[E].column] + elseif not G then + a = columns[records[A].column] + b = columns[records[B].column] + c = columns[records[C].column] + d = columns[records[D].column] + e = columns[records[E].column] + f = columns[records[F].column] + elseif not H then + a = columns[records[A].column] + b = columns[records[B].column] + c = columns[records[C].column] + d = columns[records[D].column] + e = columns[records[E].column] + f = columns[records[F].column] + g = columns[records[G].column] + elseif H then + a = columns[records[A].column] + b = columns[records[B].column] + c = columns[records[D].column] + e = columns[records[E].column] + f = columns[records[F].column] + g = columns[records[G].column] + h = columns[records[H].column] + end end - local function world_query_iter() - return world_query_next - end + local world_query_iter_next + + local function world_query_iter_create() + if not B then + function world_query_iter_next(): any + local entityId = entities[i] + while entityId == nil do + lastArchetype += 1 + archetype = compatible_archetypes[lastArchetype] + if not archetype then + return nil + end + + entities = archetype.entities + i = #entities + if i == 0 then + continue + end + entityId = entities[i] + columns = archetype.columns + local records = archetype.records + a = columns[records[A].column] + end + + local row = i + i-=1 + + return entityId, a[row] + end + elseif not C then + function world_query_iter_next(): any + local entityId = entities[i] + while entityId == nil do + lastArchetype += 1 + archetype = compatible_archetypes[lastArchetype] + if not archetype then + return nil + end + + entities = archetype.entities + i = #entities + if i == 0 then + continue + end + entityId = entities[i] + columns = archetype.columns + local records = archetype.records + a = columns[records[A].column] + b = columns[records[B].column] + end + + local row = i + i-=1 + + return entityId, a[row], b[row] + end + elseif not D then + function world_query_iter_next(): any + local entityId = entities[i] + while entityId == nil do + lastArchetype += 1 + archetype = compatible_archetypes[lastArchetype] + if not archetype then + return nil + end + + entities = archetype.entities + i = #entities + if i == 0 then + continue + end + entityId = entities[i] + columns = archetype.columns + local records = archetype.records + a = columns[records[A].column] + b = columns[records[B].column] + c = columns[records[C].column] + end + + local row = i + i-=1 + + return entityId, a[row], b[row], c[row] + end + elseif not E then + function world_query_iter_next(): any + local entityId = entities[i] + while entityId == nil do + lastArchetype += 1 + archetype = compatible_archetypes[lastArchetype] + if not archetype then + return nil + end + + entities = archetype.entities + i = #entities + if i == 0 then + continue + end + entityId = entities[i] + columns = archetype.columns + local records = archetype.records + a = columns[records[A].column] + b = columns[records[B].column] + c = columns[records[C].column] + d = columns[records[D].column] + end + + local row = i + i-=1 + + return entityId, a[row], b[row], c[row], d[row] + end + else + function world_query_iter_next(): any + local entityId = entities[i] + while entityId == nil do + lastArchetype += 1 + archetype = compatible_archetypes[lastArchetype] + if not archetype then + return nil + end + + entities = archetype.entities + i = #entities + if i == 0 then + continue + end + entityId = entities[i] + columns = archetype.columns + local records = archetype.records + + if not F then + a = columns[records[A].column] + b = columns[records[B].column] + c = columns[records[C].column] + d = columns[records[D].column] + e = columns[records[E].column] + elseif not G then + a = columns[records[A].column] + b = columns[records[B].column] + c = columns[records[C].column] + d = columns[records[D].column] + e = columns[records[E].column] + f = columns[records[F].column] + elseif not H then + a = columns[records[A].column] + b = columns[records[B].column] + c = columns[records[C].column] + d = columns[records[D].column] + e = columns[records[E].column] + f = columns[records[F].column] + g = columns[records[G].column] + elseif not I then + a = columns[records[A].column] + b = columns[records[B].column] + c = columns[records[C].column] + d = columns[records[D].column] + e = columns[records[E].column] + f = columns[records[F].column] + g = columns[records[G].column] + h = columns[records[H].column] + end + end + + local row = i + i-=1 + + if not F then + return entityId, a[row], b[row], c[row], d[row], e[row] + elseif not G then + return entityId, a[row], b[row], c[row], d[row], e[row], f[row] + elseif not H then + return entityId, a[row], b[row], c[row], d[row], e[row], f[row], g[row] + elseif not I then + return entityId, a[row], b[row], c[row], d[row], e[row], f[row], g[row], h[row] + end + + local field = archetype.records + for j, id in ids do + queryOutput[j] = columns[field[id].column][row] + end + + return entityId, unpack(queryOutput) + end + end + end local function world_query_without(self, ...) - local withoutComponents = { ... } - for i = #compatible_archetypes, 1, -1 do - local archetype = compatible_archetypes[i] - local records = archetype.records - local shouldRemove = false + local withoutComponents = { ... } + for i = #compatible_archetypes, 1, -1 do + local archetype = compatible_archetypes[i] + local records = archetype.records + local shouldRemove = false - for _, componentId in withoutComponents do - if records[componentId] then - shouldRemove = true - break + for _, componentId in withoutComponents do + if records[componentId] then + shouldRemove = true + break + end + end + + if shouldRemove then + local last = #compatible_archetypes + if last ~= i then + compatible_archetypes[i] = compatible_archetypes[last] + end + compatible_archetypes[last] = nil end - end + end - if shouldRemove then - local last = #compatible_archetypes - if last ~= i then - compatible_archetypes[i] = compatible_archetypes[last] - column_indices[i] = column_indices[last] - end - compatible_archetypes[last] = nil - column_indices[last] = nil - end - end - - lastArchetype = 1 - archetype = compatible_archetypes[lastArchetype] - - if not archetype then - return EmptyQuery - end - - return self + return self end local function world_query_replace_values(row, columns, ...) @@ -859,71 +1033,75 @@ do end end - local function world_query_replace(_, fn: (...any) -> (...any)) - for i, archetype in compatible_archetypes do - local tr = column_indices[i] - local columns = archetype.columns + local function world_query_replace(query, fn: (...any) -> (...any)) + query_init(query) - for row in archetype.entities do - if queryLength == 1 then - local a = columns[tr[1]] - local pa = fn(a[row]) + for i, archetype in compatible_archetypes do + local columns = archetype.columns + local tr = archetype.records + for row in archetype.entities do + if not B then + local va = columns[tr[A].column] + local pa = fn(va[row]) - a[row] = pa - elseif queryLength == 2 then - local a = columns[tr[1]] - local b = columns[tr[2]] + va[row] = pa + elseif not C then + local va = columns[tr[A].column] + local vb = columns[tr[B].column] - a[row], b[row] = fn(a[row], b[row]) - elseif queryLength == 3 then - local a = columns[tr[1]] - local b = columns[tr[2]] - local c = columns[tr[3]] + va[row], vb[row] = fn(va[row], vb[row]) + elseif not D then + local va = columns[tr[A].column] + local vb = columns[tr[B].column] + local vc = columns[tr[C].column] - a[row], b[row], c[row] = fn(a[row], b[row], c[row]) - elseif queryLength == 4 then - local a = columns[tr[1]] - local b = columns[tr[2]] - local c = columns[tr[3]] - local d = columns[tr[4]] + va[row], vb[row], vc[row] = fn(va[row], vb[row], vc[row]) + elseif not E then + local va = columns[tr[A].column] + local vb = columns[tr[B].column] + local vc = columns[tr[C].column] + local vd = columns[tr[D].column] - a[row], b[row], c[row], d[row] = fn( - a[row], b[row], c[row], d[row]) - else - for i = 1, queryLength do - queryOutput[i] = columns[tr[i]][row] - end - world_query_replace_values(row, columns, - fn(unpack(queryOutput))) - end + va[row], vb[row], vc[row], vd[row] = fn( + va[row], vb[row], vc[row], vd[row]) + else + local field = archetype.records + for j, id in ids do + queryOutput[j] = columns[field[id].column][row] + end + world_query_replace_values(row, columns, + fn(unpack(queryOutput))) + end + end end - end end local function world_query_with(query, ...) - local ids = { ... } - for i = #compatible_archetypes, 1, -1 do - local archetype = compatible_archetypes[i] - local records = archetype.records - local shouldRemove = false + local ids = { ... } + for i = #compatible_archetypes, 1, -1 do + local archetype = compatible_archetypes[i] + local records = archetype.records + local shouldRemove = false - for _, id in ids do - if not records[id] then - shouldRemove = true - break - end - end + for _, id in ids do + if not records[id] then + shouldRemove = true + break + end + end - if shouldRemove then - table.remove(compatible_archetypes, i) - end - end + if shouldRemove then + local last = #compatible_archetypes + if last ~= i then + compatible_archetypes[i] = compatible_archetypes[last] + end + compatible_archetypes[last] = nil + end + end - if #compatible_archetypes == 0 then - return EmptyQuery - end + query_init(query) - return query + return query end -- Meant for directly iterating over archetypes to minimize @@ -933,8 +1111,27 @@ do return compatible_archetypes end + local function world_query_drain(query) + drain = true + query_init(query) + return query + end + + local function world_query_iter(query) + query_init(query) + return world_query_iter_next + end + + local function world_query_next() + if not drain then + error("Did you forget to call query:drain()?") + end + return world_query_iter_next() + end + local it = { __iter = world_query_iter, + drain = world_query_drain, next = world_query_next, with = world_query_with, without = world_query_without, @@ -950,11 +1147,12 @@ do error("Missing components") end - local indices = {} compatible_archetypes = {} local length = 0 local components = { ... } :: any + A, B, C, D, E, F, G, H, I = ... + local archetypes = world.archetypes local firstArchetypeMap: ArchetypeMap @@ -975,7 +1173,6 @@ do local compatibleArchetype = archetypes[id] local archetypeRecords = compatibleArchetype.records - local records = {} local skip = false for i, componentId in components do @@ -984,8 +1181,6 @@ do skip = true break end - -- index should be index.offset - records[i] = index end if skip then @@ -994,24 +1189,13 @@ do length += 1 compatible_archetypes[length] = compatibleArchetype - indices[length] = records end - column_indices = indices + drain = false + init = false ids = components - lastArchetype = 1 - archetype = compatible_archetypes[lastArchetype] - - if not archetype then - return EmptyQuery - end - - queryOutput = {} - queryLength = #ids - - entities = archetype.entities - i = #entities + world_query_iter_create() return it end @@ -1187,7 +1371,7 @@ function World.new() ROOT_ARCHETYPE = (nil :: any) :: Archetype, }, World) - self.ROOT_ARCHETYPE = archetype_of(self, {}) + self.ROOT_ARCHETYPE = archetype_create(self, {}) for i = HI_COMPONENT_ID + 1, EcsRest do -- Initialize built-in components diff --git a/test/tests.luau b/test/tests.luau index 94fc6ad..7231bff 100644 --- a/test/tests.luau +++ b/test/tests.luau @@ -20,11 +20,137 @@ local function CHECK_NO_ERR(s: string, fn: (T...) -> (), ...: T...) CHECK(msg == s, 2) end end -local N = 10 +local N = 2^8 type World = jecs.WorldShim -TEST("world", function() +local function debug_world_inspect(world) + local function record(e) + return world.entityIndex.sparse[e] + end + local function tbl(e) + return record(e).archetype + end + local function archetype(e) + return tbl(e).type + end + local function records(e) + return tbl(e).records + end + local function columns(e) + return tbl(e).columns + end + local function row(e) + return record(e).row + end + + -- Important to order them in the order of their columns + local function tuple(e, ...) + for i, column in columns(e) do + if select(i, ...) ~= column[row(e)] then + return false + end + end + return true + end + + return { + record = record, + tbl = tbl, + archetype = archetype, + records = records, + row = row, + tuple = tuple, + } +end + +TEST("world:entity()", function() + do CASE "unique IDs" + local world = jecs.World.new() + local set = {} + for i = 1, N do + local e = world:entity() + CHECK(not set[e]) + set[e] = true + end + end + do CASE "generations" + local world = jecs.World.new() + local e = world:entity() + CHECK(ECS_ID(e) == 1 + jecs.Rest) + CHECK(getAlive(world.entityIndex, ECS_ID(e)) == e) + CHECK(ECS_GENERATION(e) == 0) -- 0 + e = ECS_GENERATION_INC(e) + CHECK(ECS_GENERATION(e) == 1) -- 1 + end + + do CASE "pairs" + local world = jecs.World.new() + local _e = world:entity() + local e2 = world:entity() + local e3 = world:entity() + + -- Incomplete pair, must have a bit flag that notes it is a pair + CHECK(IS_PAIR(world:entity()) == false) + + local pair = pair(e2, e3) + CHECK(IS_PAIR(pair) == true) + + CHECK(ecs_pair_relation(world.entityIndex, pair) == e2) + CHECK(ecs_pair_object(world.entityIndex, pair) == e3) + end +end) + +TEST("world:set()", function() + do CASE "archetype move" + do + local world = jecs.World.new() + + local d = debug_world_inspect(world) + + local _1 = world:component() + local _2 = world:component() + local e = world:entity() + -- An entity starts without an archetype or row + -- should therefore not need to copy over data + CHECK(d.tbl(e) == nil) + CHECK(d.row(e) == nil) + + local archetypes = #world.archetypes + -- This should create a new archetype since it is the first + -- entity to have moved there + world:set(e, _1, 1) + local oldRow = d.row(e) + local oldArchetype = d.archetype(e) + CHECK(#world.archetypes == archetypes + 1) + CHECK(oldArchetype == "1") + CHECK(d.tbl(e)) + CHECK(oldRow == 1) + + world:set(e, _2, 2) + CHECK(d.archetype(e) == "1_2") + -- Should have tuple of fields to the next archetype and set the component data + CHECK(d.tuple(e, 1, 2)) + -- Should have moved the data from the old archetype + CHECK(world.archetypeIndex[oldArchetype].columns[_1][oldRow] == nil) + end + end + + do CASE "arbitrary order" + local world = jecs.World.new() + + local Health = world:entity() + local Poison = world:component() + + local id = world:entity() + world:set(id, Poison, 5) + world:set(id, Health, 50) + + CHECK(world:get(id, Poison) == 5) + end +end) + +TEST("world:remove()", function() do CASE "should allow remove a component that doesn't exist on entity" local world = jecs.World.new() @@ -43,117 +169,174 @@ TEST("world", function() CHECK(world:get(id, Poison) == nil) CHECK(world:get(id, Health) == 50) end - do CASE("should find every component id") - local world = jecs.World.new() :: World - local A = world:component() - local B = world:component() - world:entity() - world:entity() - world:entity() +end) - local count = 0 - for componentId in world:query(jecs.Component) do - if componentId ~= A and componentId ~= B then - error("found entity") - end - count += 1 - end +TEST("world:add()", function() + do CASE "idempotent" + local world = jecs.World.new() + local d = debug_world_inspect(world) + local _1, _2 = world:component(), world:component() - CHECK(count == 2) - end + local e = world:entity() + world:add(e, _1) + world:add(e, _2) + world:add(e, _2) -- should have 0 effects + CHECK(d.archetype(e) == "1_2") + end - do CASE("should remove its components") - local world = jecs.World.new() :: World - local A = world:component() - local B = world:component() + do CASE "archetype move" + do + local world = jecs.World.new() - local e = world:entity() + local d = debug_world_inspect(world) - world:set(e, A, true) - world:set(e, B, true) + local _1 = world:component() + local e = world:entity() + -- An entity starts without an archetype or row + -- should therefore not need to copy over data + CHECK(d.tbl(e) == nil) + CHECK(d.row(e) == nil) - CHECK(world:get(e, A)) - CHECK(world:get(e, B)) + local archetypes = #world.archetypes + -- This should create a new archetype + world:add(e, _1) + CHECK(#world.archetypes == archetypes + 1) - world:clear(e) - CHECK(world:get(e, A) == nil) - CHECK(world:get(e, B) == nil) - end + CHECK(d.archetype(e) == "1") + CHECK(d.tbl(e)) + end + end +end) - do CASE("should drain query while iterating") - local world = jecs.World.new() :: World - local A = world:component() - local B = world:component() - local eA = world:entity() - world:set(eA, A, true) - local eB = world:entity() - world:set(eB, B, true) - local eAB = world:entity() - world:set(eAB, A, true) - world:set(eAB, B, true) +TEST("world:query()", function() + do CASE "query single component" + do + local world = jecs.World.new() + local A = world:component() + local B = world:component() - local q = world:query(A) + local entities = {} + for i = 1, N do + local id = world:entity() - local i = 0 - local j = 0 - for _ in q do - i+=1 - end - for _ in q do - j+=1 - end - CHECK(i == 2) - CHECK(j == 0) - end + world:set(id, A, true) + if i > 5 then + world:set(id, B, true) + end + entities[i] = id + end - do CASE("should be able to get next results") - local world = jecs.World.new() :: World - world:component() - local A = world:component() - local B = world:component() - local eA = world:entity() - world:set(eA, A, true) - local eB = world:entity() - world:set(eB, B, true) - local eAB = world:entity() - world:set(eAB, A, true) - world:set(eAB, B, true) + for id in world:query(A) do + table.remove(entities, CHECK(table.find(entities, id))) + end - local q = world:query(A) + CHECK(#entities == 0) + end - local e, data = q:next() - while e do - CHECK( - if e == eA then data == true - elseif e == eAB then data == true - else false - ) - e, data = q:next() - end - end + do + local world = jecs.World.new() :: World + local A = world:component() + local B = world:component() + local eA = world:entity() + world:set(eA, A, true) + local eB = world:entity() + world:set(eB, B, true) + local eAB = world:entity() + world:set(eAB, A, true) + world:set(eAB, B, true) - do CASE("should query all matching entities") - local world = jecs.World.new() - local A = world:component() - local B = world:component() + -- Should drain the iterator + local q = world:query(A):drain() - local entities = {} - for i = 1, N do - local id = world:entity() + local i = 0 + local j = 0 + for _ in q do + i+=1 + end + for _ in q do + j+=1 + end + CHECK(i == 2) + CHECK(j == 0) + end + end - world:set(id, A, true) - if i > 5 then - world:set(id, B, true) - end - entities[i] = id - end + do CASE "query missing component" + local world = jecs.World.new() + local A = world:component() + local B = world:component() + local C = world:component() - for id in world:query(A) do - table.remove(entities, CHECK(table.find(entities, id))) - end + local e1 = world:entity() + local e2 = world:entity() - CHECK(#entities == 0) - end + world:set(e1, A, "abc") + world:set(e2, A, "def") + world:set(e1, B, 123) + world:set(e2, B, 457) + + local counter = 0 + for _ in world:query(B, C) do + counter += 1 + end + CHECK(counter == 0) + end + + do CASE "query more than 8 components" + local world = jecs.World.new() + local components = {} + + for i = 1, 9 do + local id = world:component() + components[i] = id + end + local e = world:entity() + for i, id in components do + world:set(e, id, 13^i) + end + + for entity, a, b, c, d, e, f, g, h, i in world:query(unpack(components)) do + CHECK(a == 13^1) + CHECK(b == 13^2) + CHECK(c == 13^3) + CHECK(d == 13^4) + CHECK(e == 13^5) + CHECK(f == 13^6) + CHECK(g == 13^7) + CHECK(h == 13^8) + CHECK(i == 13^9) + end + end + + do CASE "should be able to get next results" + local world = jecs.World.new() :: World + world:component() + local A = world:component() + local B = world:component() + local eA = world:entity() + world:set(eA, A, true) + local eB = world:entity() + world:set(eB, B, true) + local eAB = world:entity() + world:set(eAB, A, true) + world:set(eAB, B, true) + + local q = world:query(A):drain() + + local e, data = q.next() + while e do + if e == eA then + CHECK(data) + elseif e == eAB then + CHECK(data) + else + CHECK(false) + end + + e, data = q.next() + end + CHECK(true) + end do CASE("should query all matching entities when irrelevant component is removed") local world = jecs.World.new() @@ -208,65 +391,6 @@ TEST("world", function() CHECK(#entities == 0) end - do CASE("should allow setting components in arbitrary order") - local world = jecs.World.new() - - local Health = world:entity() - local Poison = world:component() - - local id = world:entity() - world:set(id, Poison, 5) - world:set(id, Health, 50) - - CHECK(world:get(id, Poison) == 5) - end - - do CASE("should allow deleting components") - local world = jecs.World.new() - - local Health = world:entity() - local Poison = world:component() - - local id = world:entity() - world:set(id, Poison, 5) - world:set(id, Health, 50) - local id1 = world:entity() - world:set(id1, Poison, 500) - world:set(id1, Health, 50) - - world:delete(id) - - CHECK(world:get(id, Poison) == nil) - CHECK(world:get(id, Health) == nil) - CHECK(world:get(id1, Poison) == 500) - CHECK(world:get(id1, Health) == 50) - end - - do CASE("should increment generation") - local world = jecs.World.new() - local e = world:entity() - CHECK(ECS_ID(e) == 1 + jecs.Rest) - CHECK(getAlive(world.entityIndex, ECS_ID(e)) == e) - CHECK(ECS_GENERATION(e) == 0) -- 0 - e = ECS_GENERATION_INC(e) - CHECK(ECS_GENERATION(e) == 1) -- 1 - end - - do CASE("should get alive from index in the dense array") - local world = jecs.World.new() - local _e = world:entity() - local e2 = world:entity() - local e3 = world:entity() - - CHECK(IS_PAIR(world:entity()) == false) - - local pair = pair(e2, e3) - CHECK(IS_PAIR(pair) == true) - - CHECK(ecs_pair_relation(world.entityIndex, pair) == e2) - CHECK(ecs_pair_object(world.entityIndex, pair) == e3) - end - do CASE("should allow querying for relations") local world = jecs.World.new() local Eats = world:entity() @@ -392,129 +516,69 @@ TEST("world", function() CHECK(count == 2) end - do CASE "should be able to add/remove matching entity during iteration" - local world = jecs.World.new() - local Name = world:component() - for i = 1, 5 do - local e = world:entity() - world:set(e, Name, tostring(e)) - end - local count = 0 - for id, name in world:query(Name) do - count += 1 - CHECK(id == tonumber(name)) - - world:remove(id, Name) - local e = world:entity() - world:set(e, Name, tostring(e)) - end - CHECK(count == 5) - end - - do CASE "should allow adding a matching entity during iteration" - local world = jecs.World.new() - local A = world:component() - local B = world:component() - - local e1 = world:entity() - local e2 = world:entity() - world:add(e1, A) - world:add(e2, A) - world:add(e2, B) - - local count = 0 - for id in world:query(A) do - local e = world:entity() - world:add(e, A) - world:add(e, B) - count += 1 - end - - CHECK(count == 3) - end - - - do CASE "should not iterate same entity when adding component" - SKIP() - local world = jecs.World.new() - local A = world:component() - local B = world:component() - - local e1 = world:entity() - local e2 = world:entity() - world:add(e1, A) - world:add(e2, A) - world:add(e2, B) - - local count = 0 - for id in world:query(A) do - world:add(id, B) - - count += 1 - end - - CHECK(count == 2) - end - - do CASE "should replace component data" + do CASE "despawning while iterating" local world = jecs.World.new() local A = world:component() local B = world:component() - local C = world:component() - - local e = world:entity() - world:set(e, A, 1) - world:set(e, B, true) - world:set(e, C, "hello ") - - world:query(A, B, C):replace(function(a, b, c) - return a * 2, not b, c.."world" - end) - - CHECK(world:get(e, A) == 2) - CHECK(world:get(e, B) == false) - CHECK(world:get(e, C) == "hello world") - end - - do CASE "should not iterate when nothing matches query" - local world = jecs.World.new() - local A = world:component() - local B = world:component() local e1 = world:entity() + local e2 = world:entity() world:add(e1, A) + world:add(e2, A) + world:add(e2, B) local count = 0 - for id in world:query(B) do + for id in world:query(A) do + world:clear(id) count += 1 end - - CHECK(count == 0) + CHECK(count == 2) end - do CASE "should return nothing for empty iteration" - local world = jecs.World.new() - local A = world:component() - local B = world:component() + do CASE "iterator invalidation" + do CASE "adding" + SKIP() + local world = jecs.World.new() + local A = world:component() + local B = world:component() - local e1 = world:entity() - world:add(e1, A) + local e1 = world:entity() + local e2 = world:entity() + world:add(e1, A) + world:add(e2, A) + world:add(e2, B) - local query = world:query(B) - CHECK(query.next() == nil) - CHECK(query.replace() == nil) - end + local count = 0 + for id in world:query(A) do + world:add(id, B) - do CASE "should properly handle query:without for empty iteration" - local world = jecs.World.new() - local A = world:component() - local B = world:component() + count += 1 + end - local e1 = world:entity() - world:add(e1, A) + CHECK(count == 2) + end - local query = world:query(B) - CHECK(query == query:without()) + do CASE "spawning" + local world = jecs.World.new() + local A = world:component() + local B = world:component() + + local e1 = world:entity() + local e2 = world:entity() + world:add(e1, A) + world:add(e2, A) + world:add(e2, B) + + local count = 0 + for id in world:query(A) do + local e = world:entity() + world:add(e, A) + world:add(e, B) + count += 1 + end + + CHECK(count == 3) + end end do CASE "should not find any entities" @@ -535,6 +599,94 @@ TEST("world", function() CHECK(withoutCount == 0) end + do CASE "Empty Query" + do + local world = jecs.World.new() + local A = world:component() + local B = world:component() + + local e1 = world:entity() + world:add(e1, A) + + local query = world:query(B) + CHECK(query:next() == nil) + CHECK(query:replace() == nil) + CHECK(query:without() == query) + end + + do + local world = jecs.World.new() + local A = world:component() + local B = world:component() + + local e1 = world:entity() + world:add(e1, A) + + local count = 0 + for id in world:query(B) do + count += 1 + end + + CHECK(count == 0) + end + end + + do CASE "replace" + local world = jecs.World.new() + local A = world:component() + local B = world:component() + local C = world:component() + + local e = world:entity() + world:set(e, A, 1) + world:set(e, B, true) + world:set(e, C, "hello ") + + world:query(A, B, C):replace(function(a, b, c) + return a * 2, not b, c.."world" + end) + + CHECK(world:get(e, A) == 2) + CHECK(world:get(e, B) == false) + CHECK(world:get(e, C) == "hello world") + end + + do CASE "without" + do + -- REGRESSION TEST + local world = jecs.World.new() + local _1, _2, _3 = world:component(), world:component(), world:component() + + local counter = 0 + for e, a, b in world:query(_1, _2):without(_3) do + counter += 1 + end + CHECK(counter == 0) + end + end +end) + +TEST("world:clear()", function() + do CASE("should remove its components") + local world = jecs.World.new() :: World + local A = world:component() + local B = world:component() + + local e = world:entity() + + world:set(e, A, true) + world:set(e, B, true) + + CHECK(world:get(e, A)) + CHECK(world:get(e, B)) + + world:clear(e) + CHECK(world:get(e, A) == nil) + CHECK(world:get(e, B) == nil) + end +end) + +TEST("world:has()", function() do CASE "should find Tag on entity" local world = jecs.World.new() @@ -547,21 +699,54 @@ TEST("world", function() end do CASE "should return false when missing one tag" - local world = jecs.World.new() + local world = jecs.World.new() - local A = world:component() - local B = world:component() - local C = world:component() - local D = world:component() + local A = world:component() + local B = world:component() + local C = world:component() + local D = world:component() - local e = world:entity() - world:add(e, A) - world:add(e, C) - world:add(e, D) + local e = world:entity() + world:add(e, A) + world:add(e, C) + world:add(e, D) - CHECK(world:has(e, A, B, C, D) == false) - end + CHECK(world:has(e, A, B, C, D) == false) + end +end) +TEST("world:component()", function() + do CASE "only components should have EcsComponent trait" + local world = jecs.World.new() :: World + local A = world:component() + local e = world:entity() + + CHECK(world:has(A, jecs.Component)) + CHECK(not world:has(e, jecs.Component)) + end +end) + +TEST("world:delete", function() + do CASE("should allow deleting components") + local world = jecs.World.new() + + local Health = world:entity() + local Poison = world:component() + + local id = world:entity() + world:set(id, Poison, 5) + world:set(id, Health, 50) + local id1 = world:entity() + world:set(id1, Poison, 500) + world:set(id1, Health, 50) + + world:delete(id) + + CHECK(world:get(id, Poison) == nil) + CHECK(world:get(id, Health) == nil) + CHECK(world:get(id1, Poison) == 500) + CHECK(world:get(id1, Health) == 50) + end end) type Tracker = { track: (world: World, fn: (changes: { @@ -573,6 +758,25 @@ type Tracker = { track: (world: World, fn: (changes: { type Entity = number & { __nominal_type_dont_use: T } +local function diff(a, b) + local size = 0 + for k, v in a do + if b[k] ~= v then + return true + end + size += 1 + end + for k, v in b do + size -= 1 + end + + if size ~= 0 then + return true + end + + return false +end + local ChangeTracker: (world: World, component: Entity) -> Tracker do @@ -586,61 +790,50 @@ do local function changes_added() added = true - local q = world:query(T):without(PreviousT) + local q = world:query(T):without(PreviousT):drain() return function() - local id, data = q:next() + local id, data = q.next() if not id then return nil end if is_trivial == nil then is_trivial = typeof(data) ~= "table" +<<<<<<< HEAD end if not is_trivial then data = table.clone(data) +======= +>>>>>>> af8e15ef45d312ac36eb925a41d05863d707bbfe end add[id] = data + return id, data end end - local function diff(a, b) - local size = 0 - for k, v in a do - if b[k] ~= v then - return true - end - size += 1 - end - for k, v in b do - size -= 1 - end - - if size ~= 0 then - return true - end - - return false - end - local function changes_changed() - local q = world:query(T, PreviousT) + local q = world:query(T, PreviousT):drain() return function() - local id, new, old = q:next() + local id, new, old = q.next() while true do if not id then return nil end if is_trivial and new ~= old then +<<<<<<< HEAD +======= + break +>>>>>>> af8e15ef45d312ac36eb925a41d05863d707bbfe elseif diff(new, old) then break end - id, new, old = q:next() + id, new, old = q.next() end add[id] = new @@ -652,9 +845,9 @@ do local function changes_removed() removed = true - local q = world:query(PreviousT):without(T) + local q = world:query(PreviousT):without(T):drain() return function() - local id = q:next() + local id = q.next() if id then world:remove(id, PreviousT) end @@ -669,8 +862,8 @@ do } local function track(fn) - added = true - removed = true + added = false + removed = false fn(changes) @@ -702,78 +895,89 @@ do end end -TEST("changetracker", function() +TEST("changetracker:track()", function() local world = jecs.World.new() - do CASE "should allow change tracking" + do CASE "added" local Test = world:component() :: Entity<{ foo: number }> local TestTracker = ChangeTracker(world, Test) - local e = world:entity() - world:set(e, Test, { foo = 11 }) + local e1 = world:entity() + local data = { foo = 11 } + world:set(e1, Test, data) TestTracker.track(function(changes) local added = 0 - local changed = 0 - local removed = 0 - for e, data in changes.added() do + for e, test in changes.added() do added+=1 + CHECK(test == data) end for e, old, new in changes.changed() do - changed+=1 + CHECK(false) end for e in changes.removed() do - removed+=1 + CHECK(false) end CHECK(added == 1) - CHECK(changed == 0) - CHECK(removed == 0) end) + end + do CASE "changed" + local Test = world:component() :: Entity<{ foo: number }> + local TestTracker = ChangeTracker(world, Test) - for e, test in world:query(Test) do - test.foo = test.foo + 1 - end + local data = { foo = 11 } + local e1 = world:entity() + world:set(e1, Test, data) TestTracker.track(function(changes) - local added = 0 + end) + + data.foo += 1 + + TestTracker.track(function(changes) + for _ in changes.added() do + CHECK(false) + end local changed = 0 - local removed = 0 - - for e, data in changes.added() do - added+=1 - end - for e, old, new in changes.changed() do - changed+=1 - end - for e in changes.removed() do - removed+=1 - end - - CHECK(added == 0) + for e, old, new in changes.changed() do + CHECK(e == e1) + CHECK(new == data) + CHECK(old ~= new) + CHECK(diff(new, old)) + changed +=1 + end CHECK(changed == 1) - CHECK(removed == 0) end) + end + do CASE "removed" + local Test = world:component() :: Entity<{ foo: number }> + local TestTracker = ChangeTracker(world, Test) - world:remove(e, Test) + local data = { foo = 11 } + local e1 = world:entity() + world:set(e1, Test, data) TestTracker.track(function(changes) - local added = 0 - local changed = 0 + end) + + world:remove(e1, Test) + + TestTracker.track(function(changes) + for _ in changes.added() do + CHECK(false) + end + for _ in changes.changed() do + CHECK(false) + end local removed = 0 - for e, data in changes.added() do - added+=1 - end - for e, old, new in changes.changed() do - changed+=1 - end - for e in changes.removed() do - removed+=1 - end - CHECK(added == 0) - CHECK(changed == 0) + for e in changes.removed() do + removed += 1 + CHECK(e == e1) + end CHECK(removed == 1) end) end + end) FINISH() diff --git a/thesis/drafts/1/paper.aux b/thesis/drafts/1/paper.aux index 3d964cc..0666408 100644 --- a/thesis/drafts/1/paper.aux +++ b/thesis/drafts/1/paper.aux @@ -17,10 +17,10 @@ \@writefile{toc}{\contentsline {subsection}{\tocsubsection {}{2.1}{Research Approach}}{3}{subsection.2.1}\protected@file@percent } \@writefile{toc}{\contentsline {subsection}{\tocsubsection {}{2.2}{Research Process}}{3}{subsection.2.2}\protected@file@percent } \@writefile{toc}{\contentsline {section}{\tocsection {}{3}{Theory}}{3}{section.3}\protected@file@percent } +\citation{Nystrom} \citation{Flecs} \citation{Flecs} -\citation{Flecs} -\citation{Anderson} +\citation{ABC} \@writefile{toc}{\contentsline {subsection}{\tocsubsection {}{3.1}{Entity Component System Architecture}}{4}{subsection.3.1}\protected@file@percent } \@writefile{toc}{\contentsline {subsection}{\tocsubsection {}{3.2}{Cache Locality}}{4}{subsection.3.2}\protected@file@percent } \@writefile{toc}{\contentsline {subsection}{\tocsubsection {}{3.3}{Data Layouts}}{4}{subsection.3.3}\protected@file@percent } @@ -34,6 +34,7 @@ \@writefile{toc}{\contentsline {subsection}{\tocsubsection {}{3.7}{Sparse Set}}{6}{subsection.3.7}\protected@file@percent } \newlabel{Fig 2: Sparse Set}{{3.7}{6}{Sparse Set}{subsection.3.7}{}} \citation{Caini} +\citation{Luau} \newlabel{Fig 3: Removing Entity}{{3.7}{7}{Sparse Set}{subsection.3.7}{}} \@writefile{toc}{\contentsline {section}{\tocsection {}{4}{Implementation}}{7}{section.4}\protected@file@percent } \@writefile{toc}{\contentsline {subsection}{\tocsubsection {}{4.1}{Data Structures}}{7}{subsection.4.1}\protected@file@percent } @@ -50,16 +51,18 @@ \@writefile{toc}{\contentsline {subsection}{\tocsubsection {}{5.3}{Queries}}{11}{subsection.5.3}\protected@file@percent } \bibcite{Martin}{1} \bibcite{Muratori}{2} -\bibcite{Flecs}{3} -\bibcite{Anderson}{4} -\bibcite{Caini}{5} +\bibcite{ABC}{3} \newlabel{Fig 6: Queries}{{5.3}{12}{Queries}{subsection.5.3}{}} \@writefile{toc}{\contentsline {section}{\tocsection {}{6}{Conclusions}}{12}{section.6}\protected@file@percent } \@writefile{toc}{\contentsline {section}{\tocsection {}{7}{Acknowledgments}}{12}{section.7}\protected@file@percent } \@writefile{toc}{\contentsline {section}{\tocsection {}{}{References}}{12}{section*.2}\protected@file@percent } -\bibcite{Nystrom}{6} -\bibcite{gdc}{7} -\bibcite{matter}{8} +\bibcite{Archetypes}{4} +\bibcite{Anderson}{5} +\bibcite{Caini}{6} +\bibcite{Nystrom}{7} +\bibcite{gdc}{8} +\bibcite{matter}{9} +\bibcite{luau}{10} \newlabel{tocindent-1}{0pt} \newlabel{tocindent0}{12.7778pt} \newlabel{tocindent1}{17.77782pt} diff --git a/thesis/drafts/1/paper.fdb_latexmk b/thesis/drafts/1/paper.fdb_latexmk index f653638..d2944b5 100644 --- a/thesis/drafts/1/paper.fdb_latexmk +++ b/thesis/drafts/1/paper.fdb_latexmk @@ -1,12 +1,12 @@ # Fdb version 4 -["pdflatex"] 1713880547.68476 "c:/Users/Marcus/Documents/packages/jecs/thesis/drafts/1/paper.tex" "paper.pdf" "paper" 1713880549.83117 0 +["pdflatex"] 1722383502.05394 "c:/Users/Marcus/Documents/packages/jecs/thesis/drafts/1/paper.tex" "paper.pdf" "paper" 1722383503.46586 0 "../../images/archetype_graph.png" 1709688578 50172 8f93f7d24d4920bd8720f4b480771eb4 "" - "../../images/insertion.png" 1712278304 158773 c2f9fb7fae25fea3afb7e426b1d318d6 "" - "../../images/queries.png" 1712280101 205571 d976c9319fb29ae7dffc46ded3de4e55 "" + "../../images/insertion.png" 1720373630 158773 c2f9fb7fae25fea3afb7e426b1d318d6 "" + "../../images/queries.png" 1720373630 205571 d976c9319fb29ae7dffc46ded3de4e55 "" "../../images/random_access.png" 1712278385 64975 e6fbe06298c59f52a21da1b89efe1d12 "" "../../images/removed.png" 1709688578 10876 4c5ce75a368dfc9581164c9b1ace0382 "" "../../images/sparseset.png" 1709688578 9733 da4c27a8a932697883c764373b0b4e9e "" - "C:/Users/Marcus/AppData/Local/MiKTeX/fonts/map/pdftex/pdftex.map" 1712682076 81939 3d80a3cba051aa49603173dafcdf1492 "" + "C:/Users/Marcus/AppData/Local/MiKTeX/fonts/map/pdftex/pdftex.map" 1722382141 81939 3d80a3cba051aa49603173dafcdf1492 "" "C:/Users/Marcus/AppData/Local/MiKTeX/fonts/tfm/public/rsfs/rsfs10.tfm" 1712242763 688 37338d6ab346c2f1466b29e195316aa4 "" "C:/Users/Marcus/AppData/Local/MiKTeX/fonts/tfm/public/rsfs/rsfs5.tfm" 1712242764 684 3a51bd4fd9600428d5264cf25f04bb9a "" "C:/Users/Marcus/AppData/Local/MiKTeX/fonts/tfm/public/rsfs/rsfs7.tfm" 1712242763 692 1b6510779f0f05e9cbf03e0f6c8361e6 "" @@ -73,6 +73,9 @@ "C:/Users/Marcus/AppData/Local/Programs/MiKTeX/tex/generic/pdfescape/pdfescape.sty" 1575926700 19007 15924f7228aca6c6d184b115f4baa231 "" "C:/Users/Marcus/AppData/Local/Programs/MiKTeX/tex/generic/pdftexcmds/pdftexcmds.sty" 1623005277 20089 80423eac55aa175305d35b49e04fe23b "" "C:/Users/Marcus/AppData/Local/Programs/MiKTeX/tex/generic/uniquecounter/uniquecounter.sty" 1576434012 7008 f92eaa0a3872ed622bbf538217cd2ab7 "" + "C:/Users/Marcus/AppData/Local/Programs/MiKTeX/tex/generic/xkeyval/keyval.tex" 1656236919 2725 1a42bd9e7e57e25fc7763c445f4b785b "" + "C:/Users/Marcus/AppData/Local/Programs/MiKTeX/tex/generic/xkeyval/xkeyval.tex" 1656236919 19231 27205ee17aaa2902aea3e0c07a3cfc65 "" + "C:/Users/Marcus/AppData/Local/Programs/MiKTeX/tex/generic/xkeyval/xkvutils.tex" 1656236919 7677 9cb1a74d945bc9331f2181c0a59ff34a "" "C:/Users/Marcus/AppData/Local/Programs/MiKTeX/tex/generic/xypic/xy.sty" 1381022313 4692 1e1bcf75c622af1eefd9169948208302 "" "C:/Users/Marcus/AppData/Local/Programs/MiKTeX/tex/generic/xypic/xy.tex" 1381022313 115380 413d5f789929a45aab7d12ce0d0aee7d "" "C:/Users/Marcus/AppData/Local/Programs/MiKTeX/tex/generic/xypic/xy2cell.tex" 1381022313 28208 66beb10e89ca3b367faccdfebe2d3965 "" @@ -110,6 +113,8 @@ "C:/Users/Marcus/AppData/Local/Programs/MiKTeX/tex/latex/auxhook/auxhook.sty" 1576538732 3935 57aa3c3e203a5c2effb4d2bd2efbc323 "" "C:/Users/Marcus/AppData/Local/Programs/MiKTeX/tex/latex/base/atbegshi-ltx.sty" 1705273578 3045 273c666a54e60b9f730964f431a56c1b "" "C:/Users/Marcus/AppData/Local/Programs/MiKTeX/tex/latex/base/atveryend-ltx.sty" 1705273579 2462 6bc53756156dbd71c1ad550d30a3b93f "" + "C:/Users/Marcus/AppData/Local/Programs/MiKTeX/tex/latex/base/inputenc.sty" 1705273578 5048 425739d70251273bf93e3d51f3c40048 "" + "C:/Users/Marcus/AppData/Local/Programs/MiKTeX/tex/latex/breakurl/breakurl.sty" 1366019824 8782 9af34887a0e6e535b004c39238830991 "" "C:/Users/Marcus/AppData/Local/Programs/MiKTeX/tex/latex/comment/comment.sty" 1468691282 10197 204f75d5d8d88aa345a8c402e879e63b "" "C:/Users/Marcus/AppData/Local/Programs/MiKTeX/tex/latex/epstopdf-pkg/epstopdf-base.sty" 1623003186 13886 d1306dcf79a944f6988e688c1785f9ce "" "C:/Users/Marcus/AppData/Local/Programs/MiKTeX/tex/latex/etoolbox/etoolbox.sty" 1601897756 46845 3b58f70c6e861a13d927bff09d35ecbc "" @@ -146,12 +151,13 @@ "C:/Users/Marcus/AppData/Local/Programs/MiKTeX/tex/latex/ulem/ulem.sty" 1578651445 15682 94f55b803e160cf7fb6e4d77d07cfe1d "" "C:/Users/Marcus/AppData/Local/Programs/MiKTeX/tex/latex/url/url.sty" 1388490452 12796 8edb7d69a20b857904dd0ea757c14ec9 "" "C:/Users/Marcus/AppData/Local/Programs/MiKTeX/tex/latex/xcolor/xcolor.sty" 1700127522 55487 80a65caedd3722f4c20a14a69e785d8f "" - "c:/Users/Marcus/Documents/packages/jecs/thesis/drafts/1/paper.tex" 1713880546 32297 7fd52884d5f878c62fb7b3eb83c485d2 "" - "listings-rust.sty" 1712261101 12276 f346af5561f91e34970cbe0b79654ec2 "" - "paper.aux" 1713880549 5542 ad4da62e73d0c1bfa419d595ce41ac50 "pdflatex" - "paper.out" 1713880549 3695 a11dbc9d88dd30c22755dc5ebf6964ec "pdflatex" - "paper.tex" 1713880546 32297 7fd52884d5f878c62fb7b3eb83c485d2 "" - "paper.toc" 1713880549 3025 f0a34bc8923dbdfdaeb8258045835a7e "pdflatex" + "C:/Users/Marcus/AppData/Local/Programs/MiKTeX/tex/latex/xkeyval/xkeyval.sty" 1656236919 4937 4ce600ce9bd4ec84d0250eb6892fcf4f "" + "c:/Users/Marcus/Documents/packages/jecs/thesis/drafts/1/paper.tex" 1722383500 33628 2358f35913ab57bac270409214a52615 "" + "listings-rust.sty" 1720461559 12349 f346af5561f91e34970cbe0b79654ec2 "" + "paper.aux" 1722383503 5596 e71f1baf7c13471206b3537d383c78e2 "pdflatex" + "paper.out" 1722383503 3695 a11dbc9d88dd30c22755dc5ebf6964ec "pdflatex" + "paper.tex" 1722383500 33628 2358f35913ab57bac270409214a52615 "" + "paper.toc" 1722383503 3025 f0a34bc8923dbdfdaeb8258045835a7e "pdflatex" (generated) "paper.aux" "paper.log" diff --git a/thesis/drafts/1/paper.fls b/thesis/drafts/1/paper.fls index 2b8f5e2..ec658e8 100644 --- a/thesis/drafts/1/paper.fls +++ b/thesis/drafts/1/paper.fls @@ -42,73 +42,24 @@ INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\ulem\ulem.sty INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\latex-fonts\lasy6.tfm INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\url\url.sty INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\url\url.sty -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\listings\listings.sty -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\listings\listings.sty -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\graphics\keyval.sty -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\graphics\keyval.sty -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\listings\lstmisc.sty -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\listings\lstmisc.sty -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\listings\lstmisc.sty -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\listings\listings.cfg -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\listings\listings.cfg -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\listings\listings.cfg -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xy.sty -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xy.sty -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xy.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyrecat.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyidioms.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\xypic\xydash10.tfm -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\xypic\xyatip10.tfm -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\xypic\xybtip10.tfm -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\xypic\xybsql10.tfm -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\xypic\xycirc10.tfm +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\breakurl\breakurl.sty +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\breakurl\breakurl.sty +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\xkeyval\xkeyval.sty +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\xkeyval\xkeyval.sty +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xkeyval\xkeyval.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xkeyval\xkvutils.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xkeyval\keyval.tex INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\iftex\ifpdf.sty INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\iftex\ifpdf.sty INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\iftex\iftex.sty INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\iftex\iftex.sty -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyall.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyall.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xycurve.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xycurve.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyframe.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyframe.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xycmtip.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xycmtip.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xytips.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xytips.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\xypic\xycmat10.tfm -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\xypic\xycmbt10.tfm -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\xypic\xyluat10.tfm -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\xypic\xylubt10.tfm -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyline.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyline.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyrotate.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyrotate.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xycolor.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xycolor.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xymatrix.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xymatrix.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyarrow.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyarrow.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xygraph.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xygraph.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyarc.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyarc.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xy2cell.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xy2cell.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xypdf.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xypdf.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xypdf-co.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xypdf-cu.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xypdf-fr.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xypdf-li.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xypdf-ro.tex -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\tools\enumerate.sty -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\tools\enumerate.sty +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\base\inputenc.sty +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\base\inputenc.sty INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\hyperref\hyperref.sty INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\hyperref\hyperref.sty INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\infwarerr\infwarerr.sty INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\infwarerr\infwarerr.sty +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\graphics\keyval.sty INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\kvsetkeys\kvsetkeys.sty INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\kvsetkeys\kvsetkeys.sty INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\kvdefinekeys\kvdefinekeys.sty @@ -160,6 +111,63 @@ INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\rerunfilecheck\rer INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\rerunfilecheck\rerunfilecheck.sty INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\uniquecounter\uniquecounter.sty INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\uniquecounter\uniquecounter.sty +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\listings\listings.sty +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\listings\listings.sty +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\listings\lstmisc.sty +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\listings\lstmisc.sty +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\listings\lstmisc.sty +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\listings\listings.cfg +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\listings\listings.cfg +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\listings\listings.cfg +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xy.sty +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xy.sty +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xy.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyrecat.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyidioms.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\xypic\xydash10.tfm +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\xypic\xyatip10.tfm +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\xypic\xybtip10.tfm +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\xypic\xybsql10.tfm +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\xypic\xycirc10.tfm +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyall.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyall.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xycurve.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xycurve.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyframe.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyframe.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xycmtip.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xycmtip.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xytips.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xytips.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\xypic\xycmat10.tfm +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\xypic\xycmbt10.tfm +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\xypic\xyluat10.tfm +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\xypic\xylubt10.tfm +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyline.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyline.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyrotate.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyrotate.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xycolor.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xycolor.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xymatrix.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xymatrix.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyarrow.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyarrow.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xygraph.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xygraph.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyarc.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xyarc.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xy2cell.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xy2cell.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xypdf.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xypdf.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xypdf-co.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xypdf-cu.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xypdf-fr.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xypdf-li.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\generic\xypic\xypdf-ro.tex +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\tools\enumerate.sty +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\tools\enumerate.sty INPUT .\listings-rust.sty INPUT listings-rust.sty INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\xcolor\xcolor.sty @@ -216,13 +224,6 @@ INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\epstopdf-pkg\epsto INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\00miktex\epstopdf-sys.cfg INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\00miktex\epstopdf-sys.cfg INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex\latex\00miktex\epstopdf-sys.cfg -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\amsfonts\cmextra\cmex7.tfm -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\amsfonts\symbols\msam10.tfm -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\amsfonts\symbols\msam7.tfm -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\amsfonts\symbols\msbm10.tfm -INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\amsfonts\symbols\msbm7.tfm -INPUT C:\Users\Marcus\AppData\Local\MiKTeX\fonts\tfm\public\rsfs\rsfs10.tfm -INPUT C:\Users\Marcus\AppData\Local\MiKTeX\fonts\tfm\public\rsfs\rsfs7.tfm INPUT .\paper.out INPUT .\paper.out INPUT paper.out @@ -231,6 +232,13 @@ OUTPUT paper.pdf INPUT .\paper.out INPUT .\paper.out OUTPUT paper.out +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\amsfonts\cmextra\cmex7.tfm +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\amsfonts\symbols\msam10.tfm +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\amsfonts\symbols\msam7.tfm +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\amsfonts\symbols\msbm10.tfm +INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\amsfonts\symbols\msbm7.tfm +INPUT C:\Users\Marcus\AppData\Local\MiKTeX\fonts\tfm\public\rsfs\rsfs10.tfm +INPUT C:\Users\Marcus\AppData\Local\MiKTeX\fonts\tfm\public\rsfs\rsfs7.tfm INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\cm\cmcsc10.tfm INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\cm\cmti8.tfm INPUT C:\Users\Marcus\AppData\Local\Programs\MiKTeX\fonts\tfm\public\cm\cmbx10.tfm diff --git a/thesis/drafts/1/paper.log b/thesis/drafts/1/paper.log index 43e926f..428e6f0 100644 --- a/thesis/drafts/1/paper.log +++ b/thesis/drafts/1/paper.log @@ -1,4 +1,4 @@ -This is pdfTeX, Version 3.141592653-2.6-1.40.25 (MiKTeX 24.1) (preloaded format=pdflatex 2024.4.4) 23 APR 2024 15:55 +This is pdfTeX, Version 3.141592653-2.6-1.40.25 (MiKTeX 24.1) (preloaded format=pdflatex 2024.4.4) 31 JUL 2024 01:51 entering extended mode restricted \write18 enabled. file:line:error style messages enabled. @@ -144,111 +144,30 @@ Package: ulem 2019/11/18 ) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/url\url.sty \Urlmuskip=\muskip17 Package: url 2013/09/16 ver 3.4 Verb mode for urls, etc. -) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/listings\listings.sty (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/graphics\keyval.sty -Package: keyval 2022/05/29 v1.15 key=value parser (DPC) -\KV@toks@=\toks29 -) -\lst@mode=\count275 -\lst@gtempboxa=\box57 -\lst@token=\toks30 -\lst@length=\count276 -\lst@currlwidth=\dimen155 -\lst@column=\count277 -\lst@pos=\count278 -\lst@lostspace=\dimen156 -\lst@width=\dimen157 -\lst@newlines=\count279 -\lst@lineno=\count280 -\lst@maxwidth=\dimen158 - (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/listings\lstmisc.sty -File: lstmisc.sty 2023/02/27 1.9 (Carsten Heinz) -\c@lstnumber=\count281 -\lst@skipnumbers=\count282 -\lst@framebox=\box58 -) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/listings\listings.cfg -File: listings.cfg 2023/02/27 1.9 listings configuration -)) -Package: listings 2023/02/27 1.9 (Carsten Heinz) - (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xy.sty (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xy.tex Bootstrap'ing: catcodes, docmode, (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xyrecat.tex) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xyidioms.tex) - - Xy-pic version 3.8.9 <2013/10/06> - Copyright (c) 1991-2013 by Kristoffer H. Rose and others - Xy-pic is free software: see the User's Guide for details. - -Loading kernel: messages; fonts; allocations: state, -\X@c=\dimen159 -\Y@c=\dimen160 -\U@c=\dimen161 -\D@c=\dimen162 -\L@c=\dimen163 -\R@c=\dimen164 -\Edge@c=\toks31 -\X@p=\dimen165 -\Y@p=\dimen166 -\U@p=\dimen167 -\D@p=\dimen168 -\L@p=\dimen169 -\R@p=\dimen170 -\Edge@p=\toks32 -\X@origin=\dimen171 -\Y@origin=\dimen172 -\X@xbase=\dimen173 -\Y@xbase=\dimen174 -\X@ybase=\dimen175 -\Y@ybase=\dimen176 -\X@min=\dimen177 -\Y@min=\dimen178 -\X@max=\dimen179 -\Y@max=\dimen180 -\lastobjectbox@=\box59 -\zerodotbox@=\box60 -\almostz@=\dimen181 - direction, -\d@X=\dimen182 -\d@Y=\dimen183 -\K@=\count283 -\KK@=\count284 -\Direction=\count285 -\K@dXdY=\dimen184 -\K@dYdX=\dimen185 -\xyread@=\read2 -\xywrite@=\write4 -\csp@=\count286 -\quotPTK@=\dimen186 - utility macros; pictures: \xy, positions, -\swaptoks@@=\toks33 -\connectobjectbox@@=\box61 - objects, -\styletoks@=\toks34 - decorations; kernel objects: directionals, circles, text; options; algorithms: directions, edges, connections; Xy-pic loaded) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/iftex\ifpdf.sty +) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/breakurl\breakurl.sty +Package: breakurl 2013/04/10 v1.40 Breakable hyperref URLs + (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/xkeyval\xkeyval.sty +Package: xkeyval 2022/06/16 v2.9 package option processing (HA) + (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xkeyval\xkeyval.tex (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xkeyval\xkvutils.tex +\XKV@toks=\toks29 +\XKV@tempa@toks=\toks30 + (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xkeyval\keyval.tex)) +\XKV@depth=\count275 +File: xkeyval.tex 2014/12/03 v2.7a key=value parser (HA) +)) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/iftex\ifpdf.sty Package: ifpdf 2019/10/25 v3.4 ifpdf legacy package. Use iftex instead. (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/iftex\iftex.sty Package: iftex 2022/02/03 v1.0f TeX engine tests )) -Package: xy 2013/10/06 Xy-pic version 3.8.9 - (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xyall.tex Xy-pic option: All features v.3.8 (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xycurve.tex Xy-pic option: Curve and Spline extension v.3.12 curve, -\crv@cnt@=\count287 -\crvpts@=\toks35 -\splinebox@=\box62 -\splineval@=\dimen187 -\splinedepth@=\dimen188 -\splinetol@=\dimen189 -\splinelength@=\dimen190 - circles, -\L@=\dimen191 - loaded) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xyframe.tex Xy-pic option: Frame and Bracket extension v.3.14 loaded) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xycmtip.tex Xy-pic option: Computer Modern tip extension v.3.7 (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xytips.tex Xy-pic option: More Tips extension v.3.11 loaded) loaded) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xyline.tex Xy-pic option: Line styles extension v.3.10 -\xylinethick@=\dimen192 - loaded) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xyrotate.tex Xy-pic option: Rotate and Scale extension v.3.8 loaded) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xycolor.tex Xy-pic option: Colour extension v.3.11 loaded) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xymatrix.tex Xy-pic option: Matrix feature v.3.14 -\Row=\count288 -\Col=\count289 -\queue@=\toks36 -\queue@@=\toks37 -\qcount@=\count290 -\qcount@@=\count291 -\matrixsize@=\count292 - loaded) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xyarrow.tex Xy-pic option: Arrow and Path feature v.3.9 path, \ar, loaded) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xygraph.tex Xy-pic option: Graph feature v.3.11 loaded) loaded) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xyarc.tex Xy-pic option: Circle, Ellipse, Arc feature v.3.8 circles, ellipses, elliptical arcs, loaded) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xy2cell.tex Xy-pic option: Two-cell feature v.3.7 two-cells, loaded) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xypdf.tex Xy-pic option: PDF driver v.1.7 Xy-pic pdf driver: `color' extension support (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xypdf-co.tex loaded) Xy-pic pdf driver: `curve' extension support (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xypdf-cu.tex loaded) Xy-pic pdf driver: `frame' extension support (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xypdf-fr.tex loaded) Xy-pic pdf driver: `line' extension support (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xypdf-li.tex loaded) Xy-pic pdf driver: `rotate' extension support (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xypdf-ro.tex loaded) loaded)) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/tools\enumerate.sty -Package: enumerate 2023/07/04 v3.00 enumerate extensions (DPC) -\@enLab=\toks38 + +Package breakurl Warning: You are using breakurl while processing via pdflatex. +(breakurl) \burl will be just a synonym of \url. +(breakurl) on input line 48. + +) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/base\inputenc.sty +Package: inputenc 2021/02/14 v1.3d Input encoding file +\inpenc@prehook=\toks31 +\inpenc@posthook=\toks32 ) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/hyperref\hyperref.sty Package: hyperref 2023-11-26 v7.01g Hypertext links for LaTeX (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/infwarerr\infwarerr.sty @@ -281,14 +200,14 @@ Package: gettitlestring 2019/12/15 v1.6 Cleanup title references (HO) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/kvoptions\kvoptions.sty Package: kvoptions 2022-06-15 v3.15 Key value format for package options (HO) )) -\c@section@level=\count293 +\c@section@level=\count276 ) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/etoolbox\etoolbox.sty Package: etoolbox 2020/10/05 v2.5k e-TeX tools for LaTeX (JAW) -\etb@tempcnta=\count294 +\etb@tempcnta=\count277 ) -\@linkdim=\dimen193 -\Hy@linkcounter=\count295 -\Hy@pagecounter=\count296 +\@linkdim=\dimen155 +\Hy@linkcounter=\count278 +\Hy@pagecounter=\count279 (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/hyperref\pd1enc.def File: pd1enc.def 2023-11-26 v7.01g Hyperref: PDFDocEncoding definition (HO) Now handling font encoding PD1 ... @@ -296,7 +215,7 @@ Now handling font encoding PD1 ... ) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/intcalc\intcalc.sty Package: intcalc 2019/12/15 v1.3 Expandable calculations with integers (HO) ) -\Hy@SavedSpaceFactor=\count297 +\Hy@SavedSpaceFactor=\count280 (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/hyperref\puenc.def File: puenc.def 2023-11-26 v7.01g Hyperref: PDF Unicode definition (HO) Now handling font encoding PU ... @@ -309,17 +228,17 @@ Package hyperref Info: Plain pages OFF on input line 4196. Package hyperref Info: Backreferencing OFF on input line 4201. Package hyperref Info: Implicit mode ON; LaTeX internals redefined. Package hyperref Info: Bookmarks ON on input line 4448. -\c@Hy@tempcnt=\count298 +\c@Hy@tempcnt=\count281 LaTeX Info: Redefining \url on input line 4786. -\XeTeXLinkMargin=\dimen194 +\XeTeXLinkMargin=\dimen156 (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/bitset\bitset.sty Package: bitset 2019/12/09 v1.3 Handle bit-vector datatype (HO) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/bigintcalc\bigintcalc.sty Package: bigintcalc 2019/12/15 v1.5 Expandable calculations on big integers (HO) )) -\Fld@menulength=\count299 -\Field@Width=\dimen195 -\Fld@charsize=\dimen196 +\Fld@menulength=\count282 +\Field@Width=\dimen157 +\Fld@charsize=\dimen158 Package hyperref Info: Hyper figures OFF on input line 6065. Package hyperref Info: Link nesting OFF on input line 6070. Package hyperref Info: Hyper index ON on input line 6073. @@ -331,9 +250,9 @@ Package hyperref Info: PDF/A mode OFF on input line 6095. Package: atbegshi-ltx 2021/01/10 v1.0c Emulation of the original atbegshi package with kernel methods ) -\Hy@abspage=\count300 -\c@Item=\count301 -\c@Hfootnote=\count302 +\Hy@abspage=\count283 +\c@Item=\count284 +\c@Hfootnote=\count285 ) Package hyperref Info: Driver (autodetected): hpdftex. (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/hyperref\hpdftex.def @@ -342,8 +261,8 @@ File: hpdftex.def 2023-11-26 v7.01g Hyperref driver for pdfTeX Package: atveryend-ltx 2020/08/19 v1.0a Emulation of the original atveryend package with kernel methods ) -\Fld@listcount=\count303 -\c@bookmark@seq@number=\count304 +\Fld@listcount=\count286 +\c@bookmark@seq@number=\count287 (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/rerunfilecheck\rerunfilecheck.sty Package: rerunfilecheck 2022-07-10 v1.10 Rerun checks for auxiliary files (HO) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/uniquecounter\uniquecounter.sty @@ -352,18 +271,116 @@ Package: uniquecounter 2019/12/15 v1.4 Provide unlimited unique counter (HO) Package uniquecounter Info: New unique counter `rerunfilecheck' on input line 285. ) \Hy@SectionHShift=\skip59 +) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/listings\listings.sty +\lst@mode=\count288 +\lst@gtempboxa=\box57 +\lst@token=\toks33 +\lst@length=\count289 +\lst@currlwidth=\dimen159 +\lst@column=\count290 +\lst@pos=\count291 +\lst@lostspace=\dimen160 +\lst@width=\dimen161 +\lst@newlines=\count292 +\lst@lineno=\count293 +\lst@maxwidth=\dimen162 + (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/listings\lstmisc.sty +File: lstmisc.sty 2023/02/27 1.9 (Carsten Heinz) +\c@lstnumber=\count294 +\lst@skipnumbers=\count295 +\lst@framebox=\box58 +) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/listings\listings.cfg +File: listings.cfg 2023/02/27 1.9 listings configuration +)) +Package: listings 2023/02/27 1.9 (Carsten Heinz) + (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xy.sty (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xy.tex Bootstrap'ing: catcodes, docmode, (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xyrecat.tex) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xyidioms.tex) + + Xy-pic version 3.8.9 <2013/10/06> + Copyright (c) 1991-2013 by Kristoffer H. Rose and others + Xy-pic is free software: see the User's Guide for details. + +Loading kernel: messages; fonts; allocations: state, +\X@c=\dimen163 +\Y@c=\dimen164 +\U@c=\dimen165 +\D@c=\dimen166 +\L@c=\dimen167 +\R@c=\dimen168 +\Edge@c=\toks34 +\X@p=\dimen169 +\Y@p=\dimen170 +\U@p=\dimen171 +\D@p=\dimen172 +\L@p=\dimen173 +\R@p=\dimen174 +\Edge@p=\toks35 +\X@origin=\dimen175 +\Y@origin=\dimen176 +\X@xbase=\dimen177 +\Y@xbase=\dimen178 +\X@ybase=\dimen179 +\Y@ybase=\dimen180 +\X@min=\dimen181 +\Y@min=\dimen182 +\X@max=\dimen183 +\Y@max=\dimen184 +\lastobjectbox@=\box59 +\zerodotbox@=\box60 +\almostz@=\dimen185 + direction, +\d@X=\dimen186 +\d@Y=\dimen187 +\K@=\count296 +\KK@=\count297 +\Direction=\count298 +\K@dXdY=\dimen188 +\K@dYdX=\dimen189 +\xyread@=\read2 +\xywrite@=\write4 +\csp@=\count299 +\quotPTK@=\dimen190 + utility macros; pictures: \xy, positions, +\swaptoks@@=\toks36 +\connectobjectbox@@=\box61 + objects, +\styletoks@=\toks37 + decorations; kernel objects: directionals, circles, text; options; algorithms: directions, edges, connections; Xy-pic loaded) +Package: xy 2013/10/06 Xy-pic version 3.8.9 + (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xyall.tex Xy-pic option: All features v.3.8 (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xycurve.tex Xy-pic option: Curve and Spline extension v.3.12 curve, +\crv@cnt@=\count300 +\crvpts@=\toks38 +\splinebox@=\box62 +\splineval@=\dimen191 +\splinedepth@=\dimen192 +\splinetol@=\dimen193 +\splinelength@=\dimen194 + circles, +\L@=\dimen195 + loaded) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xyframe.tex Xy-pic option: Frame and Bracket extension v.3.14 loaded) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xycmtip.tex Xy-pic option: Computer Modern tip extension v.3.7 (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xytips.tex Xy-pic option: More Tips extension v.3.11 loaded) loaded) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xyline.tex Xy-pic option: Line styles extension v.3.10 +\xylinethick@=\dimen196 + loaded) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xyrotate.tex Xy-pic option: Rotate and Scale extension v.3.8 loaded) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xycolor.tex Xy-pic option: Colour extension v.3.11 loaded) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xymatrix.tex Xy-pic option: Matrix feature v.3.14 +\Row=\count301 +\Col=\count302 +\queue@=\toks39 +\queue@@=\toks40 +\qcount@=\count303 +\qcount@@=\count304 +\matrixsize@=\count305 + loaded) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xyarrow.tex Xy-pic option: Arrow and Path feature v.3.9 path, \ar, loaded) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xygraph.tex Xy-pic option: Graph feature v.3.11 loaded) loaded) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xyarc.tex Xy-pic option: Circle, Ellipse, Arc feature v.3.8 circles, ellipses, elliptical arcs, loaded) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xy2cell.tex Xy-pic option: Two-cell feature v.3.7 two-cells, loaded) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xypdf.tex Xy-pic option: PDF driver v.1.7 Xy-pic pdf driver: `color' extension support (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xypdf-co.tex loaded) Xy-pic pdf driver: `curve' extension support (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xypdf-cu.tex loaded) Xy-pic pdf driver: `frame' extension support (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xypdf-fr.tex loaded) Xy-pic pdf driver: `line' extension support (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xypdf-li.tex loaded) Xy-pic pdf driver: `rotate' extension support (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/generic/xypic\xypdf-ro.tex loaded) loaded)) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/tools\enumerate.sty +Package: enumerate 2023/07/04 v3.00 enumerate extensions (DPC) +\@enLab=\toks41 ) -Package hyperref Info: Option `bookmarksnumbered' set `true' on input line 23. +Package hyperref Info: Option `bookmarksnumbered' set `true' on input line 26. Package hyperref Warning: Option `bookmarks' has already been used, -(hyperref) setting the option has no effect on input line 23. +(hyperref) setting the option has no effect on input line 26. -Package hyperref Info: Option `colorlinks' set `true' on input line 23. +Package hyperref Info: Option `colorlinks' set `true' on input line 26. Package hyperref Warning: Option `pagecolor' is not available anymore. -Package hyperref Info: Option `pdfnewwindow' set `true' on input line 23. +Package hyperref Info: Option `pdfnewwindow' set `true' on input line 26. (listings-rust.sty Package: listings-rust 2018/01/23 Custom Package ) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/xcolor\xcolor.sty @@ -384,27 +401,27 @@ Package xcolor Info: Model `HSB' substituted by `hsb' on input line 1371. Package xcolor Info: Model `Gray' substituted by `gray' on input line 1372. Package xcolor Info: Model `wave' substituted by `hsb' on input line 1373. ) -\c@thm=\count305 -\c@cor=\count306 -\c@prop=\count307 -\c@lem=\count308 -\c@prob=\count309 -\c@conj=\count310 -\c@defn=\count311 -\c@ass=\count312 -\c@asss=\count313 -\c@ax=\count314 -\c@con=\count315 -\c@exmp=\count316 -\c@notn=\count317 -\c@notns=\count318 -\c@pro=\count319 -\c@quest=\count320 -\c@rem=\count321 -\c@warn=\count322 -\c@sch=\count323 -\c@obs=\count324 -\c@conv=\count325 +\c@thm=\count306 +\c@cor=\count307 +\c@prop=\count308 +\c@lem=\count309 +\c@prob=\count310 +\c@conj=\count311 +\c@defn=\count312 +\c@ass=\count313 +\c@asss=\count314 +\c@ax=\count315 +\c@con=\count316 +\c@exmp=\count317 +\c@notn=\count318 +\c@notns=\count319 +\c@pro=\count320 +\c@quest=\count321 +\c@rem=\count322 +\c@warn=\count323 +\c@sch=\count324 +\c@obs=\count325 +\c@conv=\count326 \bibstyle{plain} (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/graphics\graphicx.sty @@ -422,7 +439,7 @@ Package graphics Info: Driver file: pdftex.def on input line 107. \Gin@req@width=\dimen198 ) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/l3backend\l3backend-pdftex.def File: l3backend-pdftex.def 2024-01-04 L3 backend support: PDF output (pdfTeX) -\l__color_backend_stack_int=\count326 +\l__color_backend_stack_int=\count327 \l__pdf_internal_box=\box63 ) @@ -432,86 +449,100 @@ LaTeX Warning: Unused global option(s): (paper.aux) \openout1 = `paper.aux'. -LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 160. -LaTeX Font Info: ... okay on input line 160. -LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 160. -LaTeX Font Info: ... okay on input line 160. -LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 160. -LaTeX Font Info: ... okay on input line 160. -LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 160. -LaTeX Font Info: ... okay on input line 160. -LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 160. -LaTeX Font Info: ... okay on input line 160. -LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 160. -LaTeX Font Info: ... okay on input line 160. -LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 160. -LaTeX Font Info: ... okay on input line 160. -LaTeX Font Info: Checking defaults for PD1/pdf/m/n on input line 160. -LaTeX Font Info: ... okay on input line 160. -LaTeX Font Info: Checking defaults for PU/pdf/m/n on input line 160. -LaTeX Font Info: ... okay on input line 160. -LaTeX Font Info: Trying to load font information for U+msa on input line 160. +LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 163. +LaTeX Font Info: ... okay on input line 163. +LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 163. +LaTeX Font Info: ... okay on input line 163. +LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 163. +LaTeX Font Info: ... okay on input line 163. +LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 163. +LaTeX Font Info: ... okay on input line 163. +LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 163. +LaTeX Font Info: ... okay on input line 163. +LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 163. +LaTeX Font Info: ... okay on input line 163. +LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 163. +LaTeX Font Info: ... okay on input line 163. +LaTeX Font Info: Checking defaults for PD1/pdf/m/n on input line 163. +LaTeX Font Info: ... okay on input line 163. +LaTeX Font Info: Checking defaults for PU/pdf/m/n on input line 163. +LaTeX Font Info: ... okay on input line 163. +LaTeX Font Info: Trying to load font information for U+msa on input line 163. (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/amsfonts\umsa.fd File: umsa.fd 2013/01/14 v3.01 AMS symbols A ) -LaTeX Font Info: Trying to load font information for U+msb on input line 160. +LaTeX Font Info: Trying to load font information for U+msb on input line 163. (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/amsfonts\umsb.fd File: umsb.fd 2013/01/14 v3.01 AMS symbols B ) -LaTeX Font Info: Trying to load font information for U+rsfs on input line 160. +LaTeX Font Info: Trying to load font information for U+rsfs on input line 163. (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/jknappen\ursfs.fd File: ursfs.fd 1998/03/24 rsfs font definition file (jk) ) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/context/base/mkii\supp-pdf.mkii [Loading MPS to PDF converter (version 2006.09.02).] -\scratchcounter=\count327 +\scratchcounter=\count328 \scratchdimen=\dimen256 \scratchbox=\box64 -\nofMPsegments=\count328 -\nofMParguments=\count329 -\everyMPshowfont=\toks39 -\MPscratchCnt=\count330 +\nofMPsegments=\count329 +\nofMParguments=\count330 +\everyMPshowfont=\toks42 +\MPscratchCnt=\count331 \MPscratchDim=\dimen257 -\MPnumerator=\count331 -\makeMPintoPDFobject=\count332 -\everyMPtoPDFconversion=\toks40 +\MPnumerator=\count332 +\makeMPintoPDFobject=\count333 +\everyMPtoPDFconversion=\toks43 ) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/epstopdf-pkg\epstopdf-base.sty Package: epstopdf-base 2020-01-24 v2.11 Base part for package epstopdf Package epstopdf-base Info: Redefining graphics rule for `.eps' on input line 485. (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/00miktex\epstopdf-sys.cfg File: epstopdf-sys.cfg 2021/03/18 v2.0 Configuration of epstopdf for MiKTeX )) -\c@lstlisting=\count333 -Package xypdf Info: Line width: 0.39998pt on input line 160. -Package hyperref Info: Link coloring ON on input line 160. +Package hyperref Info: Link coloring ON on input line 163. (paper.out) (paper.out) \@outlinefile=\write5 \openout5 = `paper.out'. +\c@lstlisting=\count334 +Package xypdf Info: Line width: 0.39998pt on input line 163. (paper.toc [1{C:/Users/Marcus/AppData/Local/MiKTeX/fonts/map/pdftex/pdftex.map}]) \tf@toc=\write6 \openout6 = `paper.toc'. - [2] [3] + + +LaTeX Warning: Citation `Flecs' on page 2 undefined on input line 225. + +[2] [3] + +LaTeX Warning: Citation `Flecs' on page 4 undefined on input line 271. + LaTeX Font Info: Font shape `OT1/cmtt/bx/n' in size <10> not available -(Font) Font shape `OT1/cmtt/m/n' tried instead on input line 271. - [4] [5] -<../../images/archetype_graph.png, id=195, 831.105pt x 240.9pt> +(Font) Font shape `OT1/cmtt/m/n' tried instead on input line 274. + +LaTeX Warning: Citation `Flecs' on page 4 undefined on input line 283. + +[4] [5] +<../../images/archetype_graph.png, id=193, 831.105pt x 240.9pt> File: ../../images/archetype_graph.png Graphic file (type png) -Package pdftex.def Info: ../../images/archetype_graph.png used on input line 370. +Package pdftex.def Info: ../../images/archetype_graph.png used on input line 374. (pdftex.def) Requested size: 332.43611pt x 96.35828pt. -<../../images/sparseset.png, id=196, 484.81125pt x 161.60374pt> +<../../images/sparseset.png, id=194, 484.81125pt x 161.60374pt> File: ../../images/sparseset.png Graphic file (type png) -Package pdftex.def Info: ../../images/sparseset.png used on input line 378. +Package pdftex.def Info: ../../images/sparseset.png used on input line 382. (pdftex.def) Requested size: 290.88899pt x 96.96298pt. [6 <../../images/archetype_graph.png (PNG copy)> <../../images/sparseset.png>] -<../../images/removed.png, id=207, 484.81125pt x 161.60374pt> +<../../images/removed.png, id=205, 484.81125pt x 161.60374pt> File: ../../images/removed.png Graphic file (type png) -Package pdftex.def Info: ../../images/removed.png used on input line 398. +Package pdftex.def Info: ../../images/removed.png used on input line 402. (pdftex.def) Requested size: 290.88899pt x 96.96298pt. - [7 <../../images/removed.png>] (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/listings\lstlang1.sty + + +LaTeX Warning: Citation `Luau' on page 7 undefined on input line 408. + +[7 <../../images/removed.png>] (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/listings\lstlang1.sty File: lstlang1.sty 2023/02/27 1.9 listings language file ) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/listings\lstlang2.sty File: lstlang2.sty 2023/02/27 1.9 listings language file @@ -520,57 +551,51 @@ File: lstlang1.sty 2023/02/27 1.9 listings language file ) (C:\Users\Marcus\AppData\Local\Programs\MiKTeX\tex/latex/listings\lstlang2.sty File: lstlang2.sty 2023/02/27 1.9 listings language file ) -Overfull \hbox (17.91617pt too wide) in paragraph at lines 468--472 +Overfull \hbox (17.91617pt too wide) in paragraph at lines 472--476 \OT1/cmr/bx/n/10 Explanation: \OT1/cmr/m/n/10 This func-tion re-trieves the record for the given en-tity from \OT1/cmtt/m/n/10 entityIndex\OT1/cmr/m/n/10 . [] [8] [9] -<../../images/random_access.png, id=290, 653.44125pt x 341.02406pt> +<../../images/random_access.png, id=288, 653.44125pt x 341.02406pt> File: ../../images/random_access.png Graphic file (type png) -Package pdftex.def Info: ../../images/random_access.png used on input line 579. +Package pdftex.def Info: ../../images/random_access.png used on input line 583. (pdftex.def) Requested size: 326.71982pt x 170.51161pt. [10] -<../../images/insertion.png, id=315, 1797.71625pt x 1188.44pt> +<../../images/insertion.png, id=313, 1797.71625pt x 1188.44pt> File: ../../images/insertion.png Graphic file (type png) -Package pdftex.def Info: ../../images/insertion.png used on input line 593. +Package pdftex.def Info: ../../images/insertion.png used on input line 597. (pdftex.def) Requested size: 359.53688pt x 237.68379pt. -<../../images/queries.png, id=316, 2229.32875pt x 990.70125pt> +<../../images/queries.png, id=314, 2229.32875pt x 990.70125pt> File: ../../images/queries.png Graphic file (type png) -Package pdftex.def Info: ../../images/queries.png used on input line 609. +Package pdftex.def Info: ../../images/queries.png used on input line 613. (pdftex.def) Requested size: 334.38489pt x 148.59877pt. - [11 <../../images/random_access.png> <../../images/insertion.png>] -Overfull \hbox (110.03029pt too wide) in paragraph at lines 640--643 -\OT1/cmr/m/n/8 Part 1. [][]$\OT1/cmtt/m/n/8 https : / / t-[]machine . org / index . php / 2007 / 09 / 03 / entity-[]systems-[]are-[]the-[]future-[]of-[]mmog-[]development-[]part-[]1/$[][] - [] - - -Underfull \hbox (badness 3503) in paragraph at lines 660--663 -[]\OT1/cmr/m/n/8 Michele Caini (2020, Au-gust). ECS back and forth. [][]$\OT1/cmtt/m/n/8 https : / / skypjack . github . io / - [] - -[12 <../../images/queries.png>] [13] (paper.aux) + [11 <../../images/random_access.png> <../../images/insertion.png>] [12 <../../images/queries.png>] [13] (paper.aux) *********** LaTeX2e <2023-11-01> patch level 1 L3 programming layer <2024-01-04> *********** + + +LaTeX Warning: There were undefined references. + Package rerunfilecheck Info: File `paper.out' has not changed. (rerunfilecheck) Checksum: A11DBC9D88DD30C22755DC5EBF6964EC;3695. ) Here is how much of TeX's memory you used: - 16877 strings out of 474486 - 245316 string characters out of 5743284 + 17163 strings out of 474486 + 250241 string characters out of 5743284 1977542 words of memory out of 5000000 - 38592 multiletter control sequences out of 15000+600000 + 38859 multiletter control sequences out of 15000+600000 569162 words of font info for 85 fonts, out of 8000000 for 9000 1302 hyphenation exceptions out of 8191 - 75i,8n,79p,7940b,2289s stack positions out of 10000i,1000n,20000p,200000b,200000s + 75i,8n,117p,7940b,2340s stack positions out of 10000i,1000n,20000p,200000b,200000s -Output written on paper.pdf (13 pages, 590398 bytes). +Output written on paper.pdf (13 pages, 600187 bytes). PDF statistics: - 437 PDF objects out of 1000 (max. 8388607) - 154 named destinations out of 1000 (max. 500000) + 441 PDF objects out of 1000 (max. 8388607) + 156 named destinations out of 1000 (max. 500000) 239 words of extra memory for PDF output out of 10000 (max. 10000000) diff --git a/thesis/drafts/1/paper.pdf b/thesis/drafts/1/paper.pdf index 0ab4081..8f7b506 100644 Binary files a/thesis/drafts/1/paper.pdf and b/thesis/drafts/1/paper.pdf differ diff --git a/thesis/drafts/1/paper.synctex.gz b/thesis/drafts/1/paper.synctex.gz index 338c8f2..233caf1 100644 Binary files a/thesis/drafts/1/paper.synctex.gz and b/thesis/drafts/1/paper.synctex.gz differ diff --git a/thesis/drafts/1/paper.tex b/thesis/drafts/1/paper.tex index dece31c..8557a26 100644 --- a/thesis/drafts/1/paper.tex +++ b/thesis/drafts/1/paper.tex @@ -2,7 +2,10 @@ \usepackage{mathrsfs,comment} \usepackage[usenames,dvipsnames]{color} \usepackage[normalem]{ulem} -\usepackage{url} +\usepackage[hyphens]{url} +\usepackage{breakurl} +\usepackage[utf8]{inputenc} % Ensure proper encoding +\usepackage{hyperref} % For clickable links \usepackage{listings} \usepackage[all,arc,2cell]{xy} \UseAllTwocells @@ -259,7 +262,7 @@ The Entity Component System (ECS) architecture provides infrastructure for repre \subsection{Cache Locality} When a CPU loads data from Random Access Memory it is stored in a cache tier (i.e. L1, L2, L3), where the lower tiers are allowed to operate faster relatively to how closely -embedded it is to the CPU.\cite{Flecs} When a program requests some memory, the CPU grabs a whole slab, usually from around 64 to 128 bytes starting from the requested address, and puts it in the CPU cache, i.e. cache line. If the next requested data is in the same slab, the CPU reads it straight from the cache, which is faster than hitting RAM. Inversely, when there is a cache miss, i.e. it is not in the same slab then the CPU cannot process the next instruction because it needs said data and waits a couple of CPU cycles until it successfully fetches it. (Nystrom, 2011). +embedded it is to the CPU.\cite{Nystrom} When a program requests some memory, the CPU grabs a whole slab, usually from around 64 to 128 bytes starting from the requested address, and puts it in the CPU cache, i.e. cache line. If the next requested data is in the same slab, the CPU reads it straight from the cache, which is faster than hitting RAM. Inversely, when there is a cache miss, i.e. it is not in the same slab then the CPU cannot process the next instruction because it needs said data and waits a couple of CPU cycles until it successfully fetches it. (Nystrom, 2011). \subsection{Data Layouts} @@ -298,7 +301,7 @@ Vectorization is where code meets the requirements to use SIMD instructions. Tho \subsection{Archetype} Storing data in contiguous arrays to maximize vectorization and SIMD is the ideal situation, -however it is a very complex problem in implementation. Below the ABC problem\cite{Anderson} +however it is a very complex problem in implementation. Below the ABC problem\cite{ABC} is demonstrated where 3 entities all have the component \texttt{A} which can be stored in a single column: @@ -363,7 +366,8 @@ references to the next archetype based on component types (see Figure 1). Each edge in the graph corresponds to a component that can be added, akin to an intersection operation on the archetype set. \[A \cap \left( B \cap C \right)\] Removal of a component from the archetype is akin to a subtraction operation from the set. \[A \cap \left( B \cap C \right) - {C}\]`. -This archetype graph facilitates $\mathsf{O}(1)$ transitions between adjacent archetypes to mitigate the cost of fragmentation. +This archetype graph facilitates $\mathsf{O}(1)$ transitions between adjacent archetypes to +mitigate the cost of structual changes. \begin{figure}[htbp] \centering @@ -401,7 +405,7 @@ This method ensures that the dense array remains tightly packed.\cite{Caini} The sparse set structure is beneficial for programs that frequently manipulate the component structures of entities. However, querying multiple components can become less efficient due to the need to load and reference each component array individually. In contrast to archetypes, which only needs to iterate over entities matching their query. \section{Implementation} -The decision to use Lua scripting language for the ECS implementation was ultimately chosen because +The decision to use Luau\cite{Luau} for the ECS implementation was ultimately chosen because a pure Lua implementation confers distinct advantages in terms of compatibility and portability. By eschewing reliance on external C or C++ libraries or bindings, we ensure that our ECS framework remains platform-agnostic and @@ -422,11 +426,11 @@ The ECS utilize several key data structures to organize and manage entities and \item \textbf{EntityIndex:} Maps entity IDs to their corresponding records. - \item \textbf{ComponentIndex}: Maps IDs to archetype records. - - \item \textbf{ArchetypeIndex}: Maps type hashes to archetype. + \item \textbf{ComponentIndex}: Maps IDs to archetype maps. \item \textbf{ArchetypeMap:} Maps archetype IDs to archetype records which is used to find the column for the corresponding component. + + \item \textbf{ArchetypeIndex}: Maps type hashes to archetype. \item \textbf{Archetypes:} Maintains a collection of archetypes indexed by their IDs. \end{itemize} @@ -634,45 +638,53 @@ meticulous evaluations of the minimal ECS implementation iterations. My thanks also extend to Eryn L. K. and Lucien Greathouse for their invaluable guidance and contributions to the Matter project. -\begin{thebibliography}{9} +\sloppy +\tolerance=2000 +\hbadness=10000 +\begin{thebibliography}{10000} \bibitem{Martin} -Adam Martin (2007, September). -Entity Systems are the future of MMOG development - \\ Part 1. +Martin, Adam (2007). +\textit{Entity Systems are the future of MMOG development - Part 1}. \url{https://t-machine.org/index.php/2007/09/03/entity-systems-are-the-future-of-mmog-development-part-1/} \bibitem{Muratori} -Casey Muratori (2014, June). -Semantic Compression. +Muratori, Casey (2014). +\textit{Semantic Compression}. \url{https://caseymuratori.com/blog_0015} -\bibitem{Flecs} -Sanders Mertens (2019). -Flecs. -\url{https://github.com/SanderMertens/flecs} +\bibitem{ABC} +Mertens, Sanders (2022). \texttt{Building Games in ECS with Entity Relationships. See the ABC problem, component index.} +\url{https://ajmmertens.medium.com/building-an-ecs-1-where-are-my-entities-and-components-63d07c7da742} + +\bibitem{Archetypes} +Mertens, Sanders (2022). \texttt{Building an ECS \#2: Archetypes and Vectorization.} +\url{https://ajmmertens.medium.com/building-an-ecs-2-archetypes-and-vectorization-fe21690805f9} \bibitem{Anderson} -Carter Anderson (2022). -Bevy. -\url{https://github.com/bevyengine/bevy} +Anderson, Carter (2022). +\textit{Bevy}. +Available at: \url{https://github.com/bevyengine/bevy} \bibitem{Caini} -Michele Caini (2020, August). -ECS back and forth. -\url{https://skypjack.github.io/2020-08-02-ecs-baf-part-9/} +Caini, Michele (2020). +\textit{ECS back and forth}. +Available at: \url{https://skypjack.github.io/2020-08-02-ecs-baf-part-9/} \bibitem{Nystrom} -Robert Nystrom (2011). -Game Programming Patterns. +Nystrom, Robert (2011). +\textit{Game Programming Patterns}. \bibitem{gdc} -Scott Bilas (2002). -A Data-Driven Object System (GDC 2002 Talk by Scott Bilas). -\url{https://www.youtube.com/watch?v=Eb4-0M2a9xE} +Bilas, Scott (2002). +\textit{A Data-Driven Object System} (GDC 2002 Talk by Scott Bilas). +Available at: \url{https://www.youtube.com/watch?v=Eb4-0M2a9xE} \bibitem{matter} -Matter, an archetypal ECS for Roblox -https://matter-ecs.github.io/matter/ +\textit{Matter, an archetypal ECS for Roblox}. +Available at: \url{https://matter-ecs.github.io/matter/} + +\bibitem{luau} \end{thebibliography}